mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-11-04 06:00:22 +00:00 
			
		
		
		
	Compare commits
	
		
			181 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 916f2f96f4 | |||
| 00cc214754 | |||
| b2e38f72b9 | |||
| e7107d238d | |||
| ed9ebdbd1a | |||
| e80676d3d2 | |||
| 02d02fa8f2 | |||
| bd783fb74f | |||
| 50386adf70 | |||
| f4ee6c2890 | |||
| d45aef9fe5 | |||
| a56cd3dddd | |||
| 419e7070ee | |||
| 612cf40b5f | |||
| 8b39882e83 | |||
| e639ae172b | |||
| d0446850ae | |||
| c48465b90b | |||
| f419fd03d2 | |||
| 494812a660 | |||
| eb78f21eec | |||
| 4bda70268b | |||
| f037ce4371 | |||
| 3d2196e35d | |||
| a74f061b02 | |||
| 11ade14676 | |||
| eb562d8784 | |||
| 1ee5b4bfd4 | |||
| d97892080b | |||
| 6f37125724 | |||
| ed1baaade7 | |||
| bb9669f8fd | |||
| bdac715d48 | |||
| acf4971298 | |||
| 249bc83a8c | |||
| 0fbb92f03f | |||
| ca27cb3f82 | |||
| 3a5771a0cc | |||
| 527a2a91ac | |||
| 6763e5c4c6 | |||
| 06918d8310 | |||
| 89ccaa1b57 | |||
| 5d0bdb9bcf | |||
| 31fdcf74a5 | |||
| afca09cc1d | |||
| 531d89d9db | |||
| 6bbbea0bc3 | |||
| e337cd98c8 | |||
| bcbab3b380 | |||
| fb63de7568 | |||
| aa45a4ab13 | |||
| 2af7e2f681 | |||
| 34fd9edce0 | |||
| 2a4cb8c5f9 | |||
| 50ea40bc3a | |||
| a77654052d | |||
| 88aafce552 | |||
| 4e95d6bfff | |||
| 38d0e34fb5 | |||
| 8fbc6b9041 | |||
| e8219d6cf4 | |||
| 6c20fc4ca6 | |||
| 85cd975492 | |||
| 1171a717fe | |||
| bbe5320312 | |||
| 00acb9fddd | |||
| de3d14dc41 | |||
| 67ff9cc9b3 | |||
| af132103a0 | |||
| 3b1124a804 | |||
| f226c2dfd6 | |||
| 69d6e63846 | |||
| 02c3d397ad | |||
| 67a1050646 | |||
| 8cd0775a6c | |||
| 162294d6c6 | |||
| c4dd19dd00 | |||
| d2314422f1 | |||
| 6fedd6f859 | |||
| e52b59665f | |||
| cda9d09689 | |||
| c9237b3f00 | |||
| 18bba66c4a | |||
| 63418c4a8a | |||
| 2e66c6f4e3 | |||
| e9c5df4c13 | |||
| bc7789ad2c | |||
| e3da761249 | |||
| 4082f65afa | |||
| 5d1cab075d | |||
| bcf67f7e59 | |||
| 7d3b1f8e75 | |||
| 119a0588cc | |||
| fab789d9c0 | |||
| ceba81c08f | |||
| a061af0558 | |||
| c7a53846ad | |||
| a683cccf0c | |||
| 50d41e35c1 | |||
| aa0e831cea | |||
| 44e26ccb4f | |||
| 2a783f6e2b | |||
| 6058d6a724 | |||
| 2e9c7eb5fa | |||
| e75465ad10 | |||
| de01ad54e9 | |||
| eeea7ddbe3 | |||
| e0b18bec05 | |||
| 410e89bba9 | |||
| 9ef19dc42b | |||
| 0337d1b82d | |||
| f5bd4c5ccb | |||
| 630f9bc0d4 | |||
| 18b4ffece1 | |||
| f64e1effa3 | |||
| 847fcbb488 | |||
| 88002ec8e7 | |||
| 7f8db6a29d | |||
| b183b82443 | |||
| 5dad27de72 | |||
| 6b66084d0e | |||
| 50b56a7c39 | |||
| 7ab7d14471 | |||
| bdcc179b7b | |||
| 55ffd4b46f | |||
| 7fc5ee70e1 | |||
| a24a335743 | |||
| ef9af71960 | |||
| 925702d315 | |||
| d50dffec8c | |||
| cef2081a13 | |||
| 06c8bde7c9 | |||
| c9bbfa3820 | |||
| eed7cfdc42 | |||
| bd9b0d16ab | |||
| ea6c33b497 | |||
| dc80ade2fb | |||
| f6a06ee8ea | |||
| 2644f27975 | |||
| 3dc68a7b8b | |||
| 97fc1d6239 | |||
| 662f4d22a3 | |||
| b70aa12be9 | |||
| 71f12f5f19 | |||
| e10504eeeb | |||
| 2dea9f3bc0 | |||
| 35c9dda5bc | |||
| e831f3949a | |||
| b0b39cc693 | |||
| fc03be3f73 | |||
| b61f6b81f1 | |||
| f5bc1c1fce | |||
| a729f9568c | |||
| 5749e00377 | |||
| ef73c24a0c | |||
| 94717ee351 | |||
| 9a18ded65b | |||
| b23220f491 | |||
| 6e6bb03246 | |||
| 1ae6bae3b8 | |||
| 1239ca3256 | |||
| 57b7797ea4 | |||
| 5ee5bfd1d5 | |||
| 7229a3e198 | |||
| bee083582f | |||
| 9d7f99f286 | |||
| 6ef403853c | |||
| 6ae7ccb9a1 | |||
| dafc50c463 | |||
| e89e2c931d | |||
| 43a67b99e4 | |||
| 46c48f4f31 | |||
| bf0fe85aa6 | |||
| 42c5bd3a7f | |||
| d170e86c8a | |||
| e3078169b1 | |||
| a33ad123f6 | |||
| 7e14fa2f5c | |||
| ba698b41e1 | |||
| e76215987e | |||
| d1a247af8c | 
							
								
								
									
										12
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,12 +0,0 @@
 | 
			
		||||
name: Regular build
 | 
			
		||||
on: [push]
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - uses: actions/setup-java@v1
 | 
			
		||||
        with:
 | 
			
		||||
          java-version: 1.8
 | 
			
		||||
      - name: Build
 | 
			
		||||
        run: ./gradlew build
 | 
			
		||||
							
								
								
									
										5
									
								
								.github/workflows/dokka_push.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/dokka_push.yml
									
									
									
									
										vendored
									
									
								
							@@ -10,7 +10,10 @@ jobs:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - uses: actions/setup-java@v1
 | 
			
		||||
        with:
 | 
			
		||||
          java-version: 1.8
 | 
			
		||||
          java-version: 11
 | 
			
		||||
      - name: Fix android 32.0.0 dx
 | 
			
		||||
        continue-on-error: true
 | 
			
		||||
        run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib  && mv d8.jar dx.jar
 | 
			
		||||
      - name: Build
 | 
			
		||||
        run: ./gradlew dokkaHtml
 | 
			
		||||
      - name: Publish KDocs
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/packages_push.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/packages_push.yml
									
									
									
									
										vendored
									
									
								
							@@ -8,7 +8,10 @@ jobs:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - uses: actions/setup-java@v1
 | 
			
		||||
        with:
 | 
			
		||||
          java-version: 1.8
 | 
			
		||||
          java-version: 11
 | 
			
		||||
      - name: Fix android 32.0.0 dx
 | 
			
		||||
        continue-on-error: true
 | 
			
		||||
        run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib  && mv d8.jar dx.jar
 | 
			
		||||
      - name: Rewrite version
 | 
			
		||||
        run: |
 | 
			
		||||
          branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
 | 
			
		||||
@@ -18,6 +21,7 @@ jobs:
 | 
			
		||||
      - name: Build
 | 
			
		||||
        run: ./gradlew build
 | 
			
		||||
      - name: Publish
 | 
			
		||||
        continue-on-error: true
 | 
			
		||||
        run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signAndroidDebugPublication -x signAndroidReleasePublication -x signKotlinMultiplatformPublication
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUBPACKAGES_USER: ${{ github.actor }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -11,5 +11,6 @@ out/
 | 
			
		||||
 | 
			
		||||
secret.gradle
 | 
			
		||||
local.properties
 | 
			
		||||
kotlin-js-store
 | 
			
		||||
 | 
			
		||||
publishing.sh
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										282
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										282
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,5 +1,287 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
## 0.9.1
 | 
			
		||||
 | 
			
		||||
* `Repos`:
 | 
			
		||||
    * `Exposed`:
 | 
			
		||||
        * Default realizations of standard interfaces for exposed DB are using public fields for now:
 | 
			
		||||
            * `ExposedReadKeyValueRepo`
 | 
			
		||||
            * `ExposedReadOneToManyKeyValueRepo`
 | 
			
		||||
            * `ExposedStandardVersionsRepoProxy`
 | 
			
		||||
        * New typealiases for one to many exposed realizations:
 | 
			
		||||
            * `ExposedReadKeyValuesRepo`
 | 
			
		||||
            * `ExposedKeyValuesRepo`
 | 
			
		||||
 | 
			
		||||
## 0.9.0
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Kotlin`: `1.5.31` -> `1.6.10`
 | 
			
		||||
    * `Coroutines`: `1.5.2` -> `1.6.0`
 | 
			
		||||
    * `Serialization`: `1.3.1` -> `1.3.2`
 | 
			
		||||
    * `Exposed`: `0.36.2` -> `0.37.2`
 | 
			
		||||
    * `Ktor`: `1.6.5` -> `1.6.7`
 | 
			
		||||
    * `Klock`: `2.4.8` -> `2.4.10`
 | 
			
		||||
 | 
			
		||||
## 0.8.9
 | 
			
		||||
 | 
			
		||||
* `Ktor`:
 | 
			
		||||
    * `Server`:
 | 
			
		||||
        * Fixes in `uniloadMultipart`
 | 
			
		||||
    * `Client`:
 | 
			
		||||
        * Fixes in `unimultipart`
 | 
			
		||||
* `FSM`:
 | 
			
		||||
    * Fixes in `DefaultUpdatableStatesMachine`
 | 
			
		||||
 | 
			
		||||
## 0.8.8
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `AppCompat`: `1.3.1` -> `1.4.0`
 | 
			
		||||
    * Android Compile SDK: `31.0.0` -> `32.0.0`
 | 
			
		||||
* `FSM`:
 | 
			
		||||
    * `DefaultStatesMachine` now is extendable
 | 
			
		||||
    * New type `UpdatableStatesMachine` with default realization`DefaultUpdatableStatesMachine`
 | 
			
		||||
 | 
			
		||||
## 0.8.7
 | 
			
		||||
 | 
			
		||||
* `Ktor`:
 | 
			
		||||
    * `Client`:
 | 
			
		||||
        * `UnifiedRequester` now have no private fields
 | 
			
		||||
        * Add preview work with multipart
 | 
			
		||||
    * `Server`
 | 
			
		||||
        * `UnifiedRouter` now have no private fields
 | 
			
		||||
        * Add preview work with multipart
 | 
			
		||||
 | 
			
		||||
## 0.8.6
 | 
			
		||||
 | 
			
		||||
* `Common`:
 | 
			
		||||
    * `Either` extensions `onFirst` and `onSecond` now accept not `crossinline` callbacks
 | 
			
		||||
    * All `joinTo` now accept not `crossinline` callbacks
 | 
			
		||||
 | 
			
		||||
## 0.8.5
 | 
			
		||||
 | 
			
		||||
* `Common`:
 | 
			
		||||
    * `repeatOnFailure`
 | 
			
		||||
 | 
			
		||||
## 0.8.4
 | 
			
		||||
 | 
			
		||||
* `Ktor`:
 | 
			
		||||
    * `Server`:
 | 
			
		||||
        * Several new `createKtorServer`
 | 
			
		||||
 | 
			
		||||
## 0.8.3
 | 
			
		||||
 | 
			
		||||
* `Common`:
 | 
			
		||||
    * Ranges intersection functionality
 | 
			
		||||
    * New type `Optional`
 | 
			
		||||
* `Pagination`:
 | 
			
		||||
    * `Pagination` now extends `ClosedRange<Int>`
 | 
			
		||||
    * `Pagination` intersection functionality
 | 
			
		||||
 | 
			
		||||
## 0.8.2
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Klock`: `2.4.7` -> `2.4.8`
 | 
			
		||||
    * `Serialization`: `1.3.0` -> `1.3.1`
 | 
			
		||||
* `FSM`:
 | 
			
		||||
    * Now it is possible to pass any `CheckableHandlerHolder` in `FSMBuilder`
 | 
			
		||||
    * Now `StatesMachine` works with `CheckableHandlerHolder` instead of `CustomizableHandlerHolder`
 | 
			
		||||
 | 
			
		||||
## 0.8.1
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Exposed`: `0.36.1` -> `0.36.2`
 | 
			
		||||
    * `Core KTX`: `1.6.0` -> `1.7.0`
 | 
			
		||||
 | 
			
		||||
## 0.8.0
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Klock`: `2.4.6` -> `2.4.7`
 | 
			
		||||
    * `Ktor`: `1.6.4` -> `1.6.5`
 | 
			
		||||
    * `Exposed`: `0.35.3` -> `0.36.1`
 | 
			
		||||
* `Common`:
 | 
			
		||||
    * Type `Either` got its own serializer
 | 
			
		||||
* `FSM`:
 | 
			
		||||
    * `Common`:
 | 
			
		||||
        * Full rework of FSM:
 | 
			
		||||
            * Now it is more flexible for checking of handler opportunity to handle state
 | 
			
		||||
            * Now machine and states managers are type-oriented
 | 
			
		||||
            * `StateHandlerHolder` has been renamed to `CheckableHandlerHolder`
 | 
			
		||||
        * Add opportunity for comfortable adding default state handler
 | 
			
		||||
 | 
			
		||||
## 0.7.4
 | 
			
		||||
 | 
			
		||||
* `Common`:
 | 
			
		||||
    * New type `Either`
 | 
			
		||||
* `Serialization`:
 | 
			
		||||
    * `TypedSerializer`
 | 
			
		||||
        * New factory fun which accept vararg pairs of type and its serializer
 | 
			
		||||
* `Repos`:
 | 
			
		||||
    * `Common` (`Android`):
 | 
			
		||||
        * `AbstractMutableAndroidCRUDRepo` flows now will have extra buffer capacity instead of reply. It means that
 | 
			
		||||
          android crud repo _WILL NOT_ send previous events to the 
 | 
			
		||||
    * `Exposed`:
 | 
			
		||||
        * New parameter `AbstractExposedWriteCRUDRepo#replyCacheInFlows`
 | 
			
		||||
        * KeyValue realization `ExposedKeyValueRepo` properties `_onNewValue` and `_onValueRemoved` now are available in
 | 
			
		||||
          inheritors
 | 
			
		||||
* `Pagination`:
 | 
			
		||||
    * `Common`:
 | 
			
		||||
        * New types `getAllBy*` for current, next and custom paging
 | 
			
		||||
 | 
			
		||||
## 0.7.3
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Exposed`: `0.35.2` -> `0.35.3`
 | 
			
		||||
 | 
			
		||||
## 0.7.2
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Klock`: `2.4.5` -> `2.4.6`
 | 
			
		||||
 | 
			
		||||
## 0.7.1
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Klock`: `2.4.3` -> `2.4.5`
 | 
			
		||||
    * `Exposed`: `0.35.1` -> `0.35.2`
 | 
			
		||||
* `Coroutines`:
 | 
			
		||||
    * `Common`:
 | 
			
		||||
        * New `Flow` - `AccumulatorFlow`
 | 
			
		||||
* `FSM`:
 | 
			
		||||
    * `Common`:
 | 
			
		||||
        * `InMemoryStatesManager` has been replaced
 | 
			
		||||
        * `StatesMachine` became an interface
 | 
			
		||||
        * New manager `DefaultStatesManager` with `DefaultStatesManagerRepo` for abstraction of manager and storing of
 | 
			
		||||
          data info
 | 
			
		||||
 | 
			
		||||
## 0.7.0
 | 
			
		||||
 | 
			
		||||
**THIS VERSION HAS MIGRATED FROM KOTLINX DATETIME TO KORLIBS KLOCK. CAREFUL**
 | 
			
		||||
 | 
			
		||||
* `Versions`
 | 
			
		||||
    * `kotlinx.datetime` -> `Klock`
 | 
			
		||||
 | 
			
		||||
## 0.6.0 DO NOT RECOMMENDED
 | 
			
		||||
 | 
			
		||||
**THIS VERSION HAS MIGRATED FROM KORLIBS KLOCK TO KOTLINX DATETIME. CAREFUL**
 | 
			
		||||
**ALL DEPRECATION HAVE BEEN REMOVED**
 | 
			
		||||
 | 
			
		||||
* `Versions`
 | 
			
		||||
    * `Klock` -> `kotlinx.datetime`
 | 
			
		||||
 | 
			
		||||
## 0.5.31
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Klock`: `2.4.2` -> `2.4.3`
 | 
			
		||||
    * `Ktor`: `1.6.3` -> `1.6.4`
 | 
			
		||||
 | 
			
		||||
## 0.5.30
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Serialization`: `1.2.2` -> `1.3.0`
 | 
			
		||||
 | 
			
		||||
## 0.5.29
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Exposed`: `0.34.2` -> `0.35.1`
 | 
			
		||||
 | 
			
		||||
## 0.5.28
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Kotlin`: `1.5.30` -> `1.5.31`
 | 
			
		||||
    * `Klock`: `2.4.1` -> `2.4.2`
 | 
			
		||||
 | 
			
		||||
## 0.5.27
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Exposed`: `0.34.1` -> `0.34.2`
 | 
			
		||||
 | 
			
		||||
## 0.5.26
 | 
			
		||||
 | 
			
		||||
* `Repos`:
 | 
			
		||||
    * `InMemory`:
 | 
			
		||||
        * `MapCRUDRepo`s and `MapKeyValueRepo`s got `protected` methods and properties instead of private
 | 
			
		||||
 | 
			
		||||
## 0.5.25
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `UUID`: `0.3.0` -> `0.3.1`
 | 
			
		||||
* `Common`:
 | 
			
		||||
    * New property `MPPFile#withoutSlashAtTheEnd`
 | 
			
		||||
    * Extension `clamp` has been deprecated
 | 
			
		||||
    * New extension `Iterable#diff`
 | 
			
		||||
* `Serialization`:
 | 
			
		||||
    * New operators `TypedSerializer#plusAssign` and `TypedSerializer#minusAssign`
 | 
			
		||||
 | 
			
		||||
## 0.5.24
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Coroutines`: `1.5.1` -> `1.5.2`
 | 
			
		||||
    * `Klock`: `2.3.4` -> `2.4.1`
 | 
			
		||||
* `Coroutines`:
 | 
			
		||||
    * New function `CoroutineScope` with safely exceptions handler as second parameter
 | 
			
		||||
 | 
			
		||||
## 0.5.23
 | 
			
		||||
 | 
			
		||||
* `Versions`:
 | 
			
		||||
    * `Exposed`: `0.33.1` -> `0.34.1`
 | 
			
		||||
* `Common`:
 | 
			
		||||
    * New extensions `Iterable#joinTo` and `Array#joinTo`
 | 
			
		||||
 | 
			
		||||
## 0.5.22
 | 
			
		||||
 | 
			
		||||
* `Versions`
 | 
			
		||||
    * `Kotlin`: `1.5.21` -> `1.5.30`
 | 
			
		||||
    * `Klock`: `2.3.2` -> `2.3.4`
 | 
			
		||||
    * `AppCompat`: `1.3.0` -> `1.3.1`
 | 
			
		||||
    * `Ktor`: `1.6.2` -> `1.6.3`
 | 
			
		||||
 | 
			
		||||
## 0.5.21
 | 
			
		||||
 | 
			
		||||
* `Versions`
 | 
			
		||||
    * `Klock`: `2.3.1` -> `2.3.2`
 | 
			
		||||
* `Serialization`
 | 
			
		||||
    * `Typed Serializer`:
 | 
			
		||||
        * `TypedSerializer` Descriptor serial name has been fixed
 | 
			
		||||
 | 
			
		||||
## 0.5.20
 | 
			
		||||
 | 
			
		||||
* `Repos`:
 | 
			
		||||
    * `Common`
 | 
			
		||||
        * `Android`:
 | 
			
		||||
            * `*OrNull` analogs of `Cursor.get*(String)` extensions have been added
 | 
			
		||||
            * Extensions `Cursor.getFloat` and `Cursor.getFloatOrNull` have been added
 | 
			
		||||
 | 
			
		||||
## 0.5.19
 | 
			
		||||
 | 
			
		||||
* `LanguageCode`:
 | 
			
		||||
    * `IetfLanguageCode` became as sealed class
 | 
			
		||||
    * `IetfLanguageCode` now override `toString` and returns its code
 | 
			
		||||
 | 
			
		||||
## 0.5.18
 | 
			
		||||
 | 
			
		||||
* `Versions`
 | 
			
		||||
    * `Kotlin Exposed`: `0.32.1` -> `0.33.1`
 | 
			
		||||
* `LanguageCode`:
 | 
			
		||||
    * Module has been created
 | 
			
		||||
 | 
			
		||||
## 0.5.17
 | 
			
		||||
 | 
			
		||||
**SINCE THIS UPDATE JS PARTS WILL BE COMPILED WITH IR COMPILER ONLY**
 | 
			
		||||
 | 
			
		||||
* `Versions`
 | 
			
		||||
    * `Kotlin`: `1.5.20` -> `1.5.21`
 | 
			
		||||
    * `Ktor`: `1.6.1` -> `1.6.2`
 | 
			
		||||
    * `Klock`: `2.2.0` -> `2.3.1`
 | 
			
		||||
    * `CryptoJS`: `4.0.0` -> `4.1.1`
 | 
			
		||||
 | 
			
		||||
## 0.5.16
 | 
			
		||||
 | 
			
		||||
* `Versions`
 | 
			
		||||
  * `Coroutines`: `1.5.0` -> `1.5.1`
 | 
			
		||||
  * `Serialization`: `1.2.1` -> `1.2.2`
 | 
			
		||||
  * `Ktor`: `1.6.0` -> `1.6.1`
 | 
			
		||||
  * `Klock`: `2.1.2` -> `2.2.0`
 | 
			
		||||
  * `Core KTX`: `1.5.0` -> `1.6.0`
 | 
			
		||||
 | 
			
		||||
## 0.5.15 HOTFIX FOR 0.5.14
 | 
			
		||||
 | 
			
		||||
* `Coroutines`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
buildscript {
 | 
			
		||||
    repositories {
 | 
			
		||||
        jcenter()
 | 
			
		||||
        google()
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
        mavenLocal()
 | 
			
		||||
@@ -8,7 +7,7 @@ buildscript {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dependencies {
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:4.1.3'
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:7.0.4'
 | 
			
		||||
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 | 
			
		||||
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
 | 
			
		||||
        classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
 | 
			
		||||
@@ -20,10 +19,8 @@ buildscript {
 | 
			
		||||
allprojects {
 | 
			
		||||
    repositories {
 | 
			
		||||
        mavenLocal()
 | 
			
		||||
        jcenter()
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
        google()
 | 
			
		||||
        maven { url "https://kotlin.bintray.com/kotlinx" }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // temporal crutch until legacy tests will be stabled or legacy target will be removed
 | 
			
		||||
 
 | 
			
		||||
@@ -12,9 +12,7 @@ package dev.inmo.micro_utils.common
 | 
			
		||||
    AnnotationTarget.PROPERTY_GETTER,
 | 
			
		||||
    AnnotationTarget.PROPERTY_SETTER,
 | 
			
		||||
    AnnotationTarget.FUNCTION,
 | 
			
		||||
    AnnotationTarget.TYPE,
 | 
			
		||||
    AnnotationTarget.TYPEALIAS,
 | 
			
		||||
    AnnotationTarget.TYPE_PARAMETER
 | 
			
		||||
    AnnotationTarget.TYPEALIAS
 | 
			
		||||
)
 | 
			
		||||
annotation class PreviewFeature(val message: String = "It is possible, that behaviour of this thing will be changed or removed in future releases")
 | 
			
		||||
 | 
			
		||||
@@ -30,8 +28,6 @@ annotation class PreviewFeature(val message: String = "It is possible, that beha
 | 
			
		||||
    AnnotationTarget.PROPERTY_GETTER,
 | 
			
		||||
    AnnotationTarget.PROPERTY_SETTER,
 | 
			
		||||
    AnnotationTarget.FUNCTION,
 | 
			
		||||
    AnnotationTarget.TYPE,
 | 
			
		||||
    AnnotationTarget.TYPEALIAS,
 | 
			
		||||
    AnnotationTarget.TYPE_PARAMETER
 | 
			
		||||
    AnnotationTarget.TYPEALIAS
 | 
			
		||||
)
 | 
			
		||||
annotation class Warning(val message: String)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
package dev.inmo.micro_utils.common
 | 
			
		||||
 | 
			
		||||
@Suppress("NOTHING_TO_INLINE")
 | 
			
		||||
inline fun <T : Comparable<T>> T.clamp(min: T, max: T): T {
 | 
			
		||||
    return when {
 | 
			
		||||
        this < min -> min
 | 
			
		||||
        this > max -> max
 | 
			
		||||
        else -> this
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -27,8 +27,8 @@ data class Diff<T> internal constructor(
 | 
			
		||||
 | 
			
		||||
private inline fun <T> performChanges(
 | 
			
		||||
    potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
 | 
			
		||||
    additionalsInOld: MutableList<T>,
 | 
			
		||||
    additionalsInNew: MutableList<T>,
 | 
			
		||||
    additionsInOld: MutableList<T>,
 | 
			
		||||
    additionsInNew: MutableList<T>,
 | 
			
		||||
    changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
 | 
			
		||||
    removedList: MutableList<IndexedValue<T>>,
 | 
			
		||||
    addedList: MutableList<IndexedValue<T>>,
 | 
			
		||||
@@ -52,20 +52,20 @@ private inline fun <T> performChanges(
 | 
			
		||||
                    newPotentials.first().second ?.let { addedList.add(it) }
 | 
			
		||||
                    newPotentials.drop(1).take(newPotentials.size - 2).forEach { (oldOne, newOne) ->
 | 
			
		||||
                        addedList.add(newOne!!)
 | 
			
		||||
                        oldOne ?.let { additionalsInOld.add(oldOne.value) }
 | 
			
		||||
                        oldOne ?.let { additionsInOld.add(oldOne.value) }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (newPotentials.size > 1) {
 | 
			
		||||
                        newPotentials.last().first ?.value ?.let { additionalsInOld.add(it) }
 | 
			
		||||
                        newPotentials.last().first ?.value ?.let { additionsInOld.add(it) }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                newOneEqualToOldObject -> {
 | 
			
		||||
                    newPotentials.first().first ?.let { removedList.add(it) }
 | 
			
		||||
                    newPotentials.drop(1).take(newPotentials.size - 2).forEach { (oldOne, newOne) ->
 | 
			
		||||
                        removedList.add(oldOne!!)
 | 
			
		||||
                        newOne ?.let { additionalsInNew.add(newOne.value) }
 | 
			
		||||
                        newOne ?.let { additionsInNew.add(newOne.value) }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (newPotentials.size > 1) {
 | 
			
		||||
                        newPotentials.last().second ?.value ?.let { additionalsInNew.add(it) }
 | 
			
		||||
                        newPotentials.last().second ?.value ?.let { additionsInNew.add(it) }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -139,6 +139,10 @@ fun <T> Iterable<T>.calculateDiff(
 | 
			
		||||
 | 
			
		||||
    return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList())
 | 
			
		||||
}
 | 
			
		||||
inline fun <T> Iterable<T>.diff(
 | 
			
		||||
    other: Iterable<T>,
 | 
			
		||||
    strictComparison: Boolean = false
 | 
			
		||||
): Diff<T> = calculateDiff(other, strictComparison)
 | 
			
		||||
 | 
			
		||||
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new)
 | 
			
		||||
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,148 @@
 | 
			
		||||
package dev.inmo.micro_utils.common
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.*
 | 
			
		||||
import kotlinx.serialization.builtins.serializer
 | 
			
		||||
import kotlinx.serialization.descriptors.*
 | 
			
		||||
import kotlinx.serialization.encoding.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Realization of this interface will contains at least one not null - [t1] or [t2]
 | 
			
		||||
 *
 | 
			
		||||
 * @see EitherFirst
 | 
			
		||||
 * @see EitherSecond
 | 
			
		||||
 * @see Either.Companion.first
 | 
			
		||||
 * @see Either.Companion.second
 | 
			
		||||
 * @see Either.onFirst
 | 
			
		||||
 * @see Either.onSecond
 | 
			
		||||
 */
 | 
			
		||||
@Serializable(EitherSerializer::class)
 | 
			
		||||
sealed interface Either<T1, T2> {
 | 
			
		||||
    val t1: T1?
 | 
			
		||||
    val t2: T2?
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun <T1, T2> serializer(
 | 
			
		||||
            t1Serializer: KSerializer<T1>,
 | 
			
		||||
            t2Serializer: KSerializer<T2>,
 | 
			
		||||
        ): KSerializer<Either<T1, T2>> = EitherSerializer(t1Serializer, t2Serializer)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EitherSerializer<T1, T2>(
 | 
			
		||||
    t1Serializer: KSerializer<T1>,
 | 
			
		||||
    t2Serializer: KSerializer<T2>,
 | 
			
		||||
) : KSerializer<Either<T1, T2>> {
 | 
			
		||||
    @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
 | 
			
		||||
    override val descriptor: SerialDescriptor = buildSerialDescriptor(
 | 
			
		||||
        "TypedSerializer",
 | 
			
		||||
        SerialKind.CONTEXTUAL
 | 
			
		||||
    ) {
 | 
			
		||||
        element("type", String.serializer().descriptor)
 | 
			
		||||
        element("value", ContextualSerializer(Either::class).descriptor)
 | 
			
		||||
    }
 | 
			
		||||
    private val t1EitherSerializer = EitherFirst.serializer(t1Serializer, t2Serializer)
 | 
			
		||||
    private val t2EitherSerializer = EitherSecond.serializer(t1Serializer, t2Serializer)
 | 
			
		||||
 | 
			
		||||
    @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
 | 
			
		||||
    override fun deserialize(decoder: Decoder): Either<T1, T2> {
 | 
			
		||||
        return decoder.decodeStructure(descriptor) {
 | 
			
		||||
            var type: String? = null
 | 
			
		||||
            lateinit var result: Either<T1, T2>
 | 
			
		||||
            while (true) {
 | 
			
		||||
                when (val index = decodeElementIndex(descriptor)) {
 | 
			
		||||
                    0 -> type = decodeStringElement(descriptor, 0)
 | 
			
		||||
                    1 -> {
 | 
			
		||||
                        result = when (type) {
 | 
			
		||||
                            "t1" -> decodeSerializableElement(
 | 
			
		||||
                                descriptor,
 | 
			
		||||
                                1,
 | 
			
		||||
                                t1EitherSerializer
 | 
			
		||||
                            )
 | 
			
		||||
                            "t2" -> decodeSerializableElement(
 | 
			
		||||
                                descriptor,
 | 
			
		||||
                                1,
 | 
			
		||||
                                t2EitherSerializer
 | 
			
		||||
                            )
 | 
			
		||||
                            else -> error("Unknown type of either: $type")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    CompositeDecoder.DECODE_DONE -> break
 | 
			
		||||
                    else -> error("Unexpected index: $index")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            result
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
 | 
			
		||||
    override fun serialize(encoder: Encoder, value: Either<T1, T2>) {
 | 
			
		||||
        encoder.encodeStructure(descriptor) {
 | 
			
		||||
            when (value) {
 | 
			
		||||
                is EitherFirst -> {
 | 
			
		||||
                    encodeStringElement(descriptor, 0, "t1")
 | 
			
		||||
                    encodeSerializableElement(descriptor, 1, t1EitherSerializer, value)
 | 
			
		||||
                }
 | 
			
		||||
                is EitherSecond -> {
 | 
			
		||||
                    encodeStringElement(descriptor, 0, "t2")
 | 
			
		||||
                    encodeSerializableElement(descriptor, 1, t2EitherSerializer, value)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This type [Either] will always have not nullable [t1]
 | 
			
		||||
 */
 | 
			
		||||
@Serializable
 | 
			
		||||
data class EitherFirst<T1, T2>(
 | 
			
		||||
    override val t1: T1
 | 
			
		||||
) : Either<T1, T2> {
 | 
			
		||||
    override val t2: T2?
 | 
			
		||||
        get() = null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This type [Either] will always have not nullable [t2]
 | 
			
		||||
 */
 | 
			
		||||
@Serializable
 | 
			
		||||
data class EitherSecond<T1, T2>(
 | 
			
		||||
    override val t2: T2
 | 
			
		||||
) : Either<T1, T2> {
 | 
			
		||||
    override val t1: T1?
 | 
			
		||||
        get() = null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @return New instance of [EitherFirst]
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T1, T2> Either.Companion.first(t1: T1): Either<T1, T2> = EitherFirst(t1)
 | 
			
		||||
/**
 | 
			
		||||
 * @return New instance of [EitherSecond]
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T1, T2> Either.Companion.second(t2: T2): Either<T1, T2> = EitherSecond(t2)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will call [block] in case when [Either.t1] of [this] is not null
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T1, T2, E : Either<T1, T2>> E.onFirst(block: (T1) -> Unit): E {
 | 
			
		||||
    val t1 = t1
 | 
			
		||||
    t1 ?.let(block)
 | 
			
		||||
    return this
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will call [block] in case when [Either.t2] of [this] is not null
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T1, T2, E : Either<T1, T2>> E.onSecond(block: (T2) -> Unit): E {
 | 
			
		||||
    val t2 = t2
 | 
			
		||||
    t2 ?.let(block)
 | 
			
		||||
    return this
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <reified T1, reified T2> Any.either() = when (this) {
 | 
			
		||||
    is T1 -> Either.first<T1, T2>(this)
 | 
			
		||||
    is T2 -> Either.second<T1, T2>(this)
 | 
			
		||||
    else -> error("Incorrect type of either argument $this")
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
package dev.inmo.micro_utils.common
 | 
			
		||||
 | 
			
		||||
inline fun <I, R> Iterable<I>.joinTo(
 | 
			
		||||
    separatorFun: (I) -> R?,
 | 
			
		||||
    prefix: R? = null,
 | 
			
		||||
    postfix: R? = null,
 | 
			
		||||
    transform: (I) -> R?
 | 
			
		||||
): List<R> {
 | 
			
		||||
    val result = mutableListOf<R>()
 | 
			
		||||
    val iterator = iterator()
 | 
			
		||||
 | 
			
		||||
    prefix ?.let(result::add)
 | 
			
		||||
 | 
			
		||||
    while (iterator.hasNext()) {
 | 
			
		||||
        val element = iterator.next()
 | 
			
		||||
        result.add(transform(element) ?: continue)
 | 
			
		||||
 | 
			
		||||
        if (iterator.hasNext()) {
 | 
			
		||||
            result.add(separatorFun(element) ?: continue)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    postfix ?.let(result::add)
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <I, R> Iterable<I>.joinTo(
 | 
			
		||||
    separator: R? = null,
 | 
			
		||||
    prefix: R? = null,
 | 
			
		||||
    postfix: R? = null,
 | 
			
		||||
    transform: (I) -> R?
 | 
			
		||||
): List<R> = joinTo({ separator }, prefix, postfix, transform)
 | 
			
		||||
 | 
			
		||||
inline fun <I> Iterable<I>.joinTo(
 | 
			
		||||
    separatorFun: (I) -> I?,
 | 
			
		||||
    prefix: I? = null,
 | 
			
		||||
    postfix: I? = null
 | 
			
		||||
): List<I> = joinTo<I, I>(separatorFun, prefix, postfix) { it }
 | 
			
		||||
 | 
			
		||||
inline fun <I> Iterable<I>.joinTo(
 | 
			
		||||
    separator: I? = null,
 | 
			
		||||
    prefix: I? = null,
 | 
			
		||||
    postfix: I? = null
 | 
			
		||||
): List<I> = joinTo<I>({ separator }, prefix, postfix)
 | 
			
		||||
 | 
			
		||||
inline fun <I, reified R> Array<I>.joinTo(
 | 
			
		||||
    separatorFun: (I) -> R?,
 | 
			
		||||
    prefix: R? = null,
 | 
			
		||||
    postfix: R? = null,
 | 
			
		||||
    transform: (I) -> R?
 | 
			
		||||
): Array<R> = asIterable().joinTo(separatorFun, prefix, postfix, transform).toTypedArray()
 | 
			
		||||
 | 
			
		||||
inline fun <I, reified R> Array<I>.joinTo(
 | 
			
		||||
    separator: R? = null,
 | 
			
		||||
    prefix: R? = null,
 | 
			
		||||
    postfix: R? = null,
 | 
			
		||||
    transform: (I) -> R?
 | 
			
		||||
): Array<R> = asIterable().joinTo(separator, prefix, postfix, transform).toTypedArray()
 | 
			
		||||
@@ -7,7 +7,7 @@ import kotlin.jvm.JvmInline
 | 
			
		||||
@JvmInline
 | 
			
		||||
value class FileName(val string: String) {
 | 
			
		||||
    val name: String
 | 
			
		||||
        get() = string.takeLastWhile { it != '/' }
 | 
			
		||||
        get() = withoutSlashAtTheEnd.takeLastWhile { it != '/' }
 | 
			
		||||
    val extension: String
 | 
			
		||||
        get() = name.takeLastWhile { it != '.' }
 | 
			
		||||
    val nameWithoutExtension: String
 | 
			
		||||
@@ -17,15 +17,18 @@ value class FileName(val string: String) {
 | 
			
		||||
                filename.substring(0, it)
 | 
			
		||||
            } ?: filename
 | 
			
		||||
        }
 | 
			
		||||
    val withoutSlashAtTheEnd: String
 | 
			
		||||
        get() = string.dropLastWhile { it == '/' }
 | 
			
		||||
    override fun toString(): String = string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@PreviewFeature
 | 
			
		||||
expect class MPPFile
 | 
			
		||||
 | 
			
		||||
expect val MPPFile.filename: FileName
 | 
			
		||||
expect val MPPFile.filesize: Long
 | 
			
		||||
expect val MPPFile.bytesAllocatorSync: ByteArrayAllocator
 | 
			
		||||
expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator
 | 
			
		||||
fun MPPFile.bytesSync() = bytesAllocatorSync()
 | 
			
		||||
suspend fun MPPFile.bytes() = bytesAllocator()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,92 @@
 | 
			
		||||
@file:Suppress("unused")
 | 
			
		||||
 | 
			
		||||
package dev.inmo.micro_utils.common
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This type represents [T] as not only potentially nullable data, but also as a data which can not be presented. This
 | 
			
		||||
 * type will be useful in cases when [T] is nullable and null as valuable data too in time of data absence should be
 | 
			
		||||
 * presented by some third type.
 | 
			
		||||
 *
 | 
			
		||||
 * Let's imagine, you have nullable name in some database. In case when name is not nullable everything is clear - null
 | 
			
		||||
 * will represent absence of row in the database. In case when name is nullable null will be a little bit dual-meaning,
 | 
			
		||||
 * cause this null will say nothing about availability of the row (of course, it is exaggerated example)
 | 
			
		||||
 *
 | 
			
		||||
 * @see Optional.presented
 | 
			
		||||
 * @see Optional.absent
 | 
			
		||||
 * @see Optional.optional
 | 
			
		||||
 * @see Optional.onPresented
 | 
			
		||||
 * @see Optional.onAbsent
 | 
			
		||||
 */
 | 
			
		||||
@Serializable
 | 
			
		||||
data class Optional<T> internal constructor(
 | 
			
		||||
    @Warning("It is unsafe to use this data directly")
 | 
			
		||||
    val data: T?,
 | 
			
		||||
    @Warning("It is unsafe to use this data directly")
 | 
			
		||||
    val dataPresented: Boolean
 | 
			
		||||
) {
 | 
			
		||||
    companion object {
 | 
			
		||||
        /**
 | 
			
		||||
         * Will create [Optional] with presented data
 | 
			
		||||
         */
 | 
			
		||||
        fun <T> presented(data: T) = Optional(data, true)
 | 
			
		||||
        /**
 | 
			
		||||
         * Will create [Optional] without data
 | 
			
		||||
         */
 | 
			
		||||
        fun <T> absent() = Optional<T>(null, false)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline val <T> T.optional
 | 
			
		||||
    get() = Optional.presented(this)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will call [block] when data presented ([Optional.dataPresented] == true)
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T> Optional<T>.onPresented(block: (T) -> Unit): Optional<T> = apply {
 | 
			
		||||
    if (dataPresented) { @Suppress("UNCHECKED_CAST") block(data as T) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will call [block] when data presented ([Optional.dataPresented] == true)
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T, R> Optional<T>.mapOnPresented(block: (T) -> R): R? = run {
 | 
			
		||||
    if (dataPresented) { @Suppress("UNCHECKED_CAST") block(data as T) } else null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will call [block] when data absent ([Optional.dataPresented] == false)
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T> Optional<T>.onAbsent(block: () -> Unit): Optional<T> = apply {
 | 
			
		||||
    if (!dataPresented) { block() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will call [block] when data presented ([Optional.dataPresented] == true)
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T, R> Optional<T>.mapOnAbsent(block: () -> R): R? = run {
 | 
			
		||||
    if (!dataPresented) { @Suppress("UNCHECKED_CAST") block() } else null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or null otherwise
 | 
			
		||||
 */
 | 
			
		||||
fun <T> Optional<T>.dataOrNull() = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else null
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or throw [throwable] otherwise
 | 
			
		||||
 */
 | 
			
		||||
fun <T> Optional<T>.dataOrThrow(throwable: Throwable) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else throw throwable
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or call [block] and returns the result of it
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T> Optional<T>.dataOrElse(block: () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block()
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or call [block] and returns the result of it
 | 
			
		||||
 */
 | 
			
		||||
@Deprecated("dataOrElse now is inline", ReplaceWith("dataOrElse", "dev.inmo.micro_utils.common.dataOrElse"))
 | 
			
		||||
suspend fun <T> Optional<T>.dataOrElseSuspendable(block: suspend () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block()
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
package dev.inmo.micro_utils.common
 | 
			
		||||
 | 
			
		||||
fun <T : Comparable<T>> ClosedRange<T>.intersect(other: ClosedRange<T>): Pair<T, T>? = when {
 | 
			
		||||
    start == other.start && endInclusive == other.endInclusive -> start to endInclusive
 | 
			
		||||
    start > other.endInclusive || other.start > endInclusive -> null
 | 
			
		||||
    else -> maxOf(start, other.start) to minOf(endInclusive, other.endInclusive)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun IntRange.intersect(
 | 
			
		||||
    other: IntRange
 | 
			
		||||
): IntRange? = (this as ClosedRange<Int>).intersect(other as ClosedRange<Int>) ?.let {
 | 
			
		||||
    it.first .. it.second
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun LongRange.intersect(
 | 
			
		||||
    other: LongRange
 | 
			
		||||
): LongRange? = (this as ClosedRange<Long>).intersect(other as ClosedRange<Long>) ?.let {
 | 
			
		||||
    it.first .. it.second
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,21 @@
 | 
			
		||||
package dev.inmo.micro_utils.common
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Executes the given [action] until getting of successful result specified number of [times].
 | 
			
		||||
 *
 | 
			
		||||
 * A zero-based index of current iteration is passed as a parameter to [action].
 | 
			
		||||
 */
 | 
			
		||||
inline fun <R> repeatOnFailure(
 | 
			
		||||
    times: Int,
 | 
			
		||||
    onEachFailure: (Throwable) -> Unit = {},
 | 
			
		||||
    action: (Int) -> R
 | 
			
		||||
): Optional<R> {
 | 
			
		||||
    repeat(times) {
 | 
			
		||||
        runCatching {
 | 
			
		||||
            action(it)
 | 
			
		||||
        }.onFailure(onEachFailure).onSuccess {
 | 
			
		||||
            return Optional.presented(it)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return Optional.absent()
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +11,7 @@ class DiffUtilsTests {
 | 
			
		||||
        val withIndex = oldList.withIndex()
 | 
			
		||||
 | 
			
		||||
        for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
 | 
			
		||||
            for ((i, v) in withIndex) {
 | 
			
		||||
            for ((i, _) in withIndex) {
 | 
			
		||||
                if (i + count > oldList.lastIndex) {
 | 
			
		||||
                    continue
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,12 @@ package dev.inmo.micro_utils.common
 | 
			
		||||
 | 
			
		||||
import org.khronos.webgl.ArrayBuffer
 | 
			
		||||
import org.w3c.dom.ErrorEvent
 | 
			
		||||
import org.w3c.files.File
 | 
			
		||||
import org.w3c.files.FileReader
 | 
			
		||||
import org.w3c.files.*
 | 
			
		||||
import kotlin.js.Promise
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual typealias MPPFile = File
 | 
			
		||||
 | 
			
		||||
fun MPPFile.readBytesPromise() = Promise<ByteArray> { success, failure ->
 | 
			
		||||
@@ -21,12 +23,32 @@ fun MPPFile.readBytesPromise() = Promise<ByteArray> { success, failure ->
 | 
			
		||||
    reader.readAsArrayBuffer(this)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun MPPFile.readBytes(): ByteArray {
 | 
			
		||||
    val reader = FileReaderSync()
 | 
			
		||||
    return reader.readAsArrayBuffer(this).toByteArray()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().await()
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual val MPPFile.filename: FileName
 | 
			
		||||
    get() = FileName(name)
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual val MPPFile.filesize: Long
 | 
			
		||||
    get() = size.toLong()
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
@Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can")
 | 
			
		||||
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
 | 
			
		||||
    get() = ::readBytes
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
@Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can")
 | 
			
		||||
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
 | 
			
		||||
    get() = ::dirtyReadBytes
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,29 @@ import dev.inmo.micro_utils.coroutines.doInIO
 | 
			
		||||
import dev.inmo.micro_utils.coroutines.doOutsideOfCoroutine
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual typealias MPPFile = File
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual val MPPFile.filename: FileName
 | 
			
		||||
    get() = FileName(name)
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual val MPPFile.filesize: Long
 | 
			
		||||
    get() = length()
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
 | 
			
		||||
    get() = ::readBytes
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
 | 
			
		||||
    get() = {
 | 
			
		||||
        doInIO {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,94 @@
 | 
			
		||||
package dev.inmo.micro_utils.coroutines
 | 
			
		||||
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.channels.BufferOverflow
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.*
 | 
			
		||||
import kotlinx.coroutines.sync.Mutex
 | 
			
		||||
import kotlinx.coroutines.sync.withLock
 | 
			
		||||
 | 
			
		||||
private sealed interface AccumulatorFlowStep
 | 
			
		||||
private data class DataRetrievedAccumulatorFlowStep(val data: Any) : AccumulatorFlowStep
 | 
			
		||||
private data class SubscribeAccumulatorFlowStep(val channel: Channel<Any>) : AccumulatorFlowStep
 | 
			
		||||
private data class UnsubscribeAccumulatorFlowStep(val channel: Channel<Any>) : AccumulatorFlowStep
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This [Flow] will have behaviour very similar to [SharedFlow], but there are several differences:
 | 
			
		||||
 *
 | 
			
		||||
 * * All unhandled by [FlowCollector] data will not be removed from [AccumulatorFlow] and will be sent to new
 | 
			
		||||
 * [FlowCollector]s until anybody will handle it
 | 
			
		||||
 * * Here there are an [activeData] where data [T] will be stored until somebody will handle it
 | 
			
		||||
 */
 | 
			
		||||
class AccumulatorFlow<T>(
 | 
			
		||||
    sourceDataFlow: Flow<T>,
 | 
			
		||||
    scope: CoroutineScope
 | 
			
		||||
) : AbstractFlow<T>() {
 | 
			
		||||
    private val subscope = scope.LinkedSupervisorScope()
 | 
			
		||||
    private val activeData = ArrayDeque<T>()
 | 
			
		||||
    private val dataMutex = Mutex()
 | 
			
		||||
    private val channelsForBroadcast = mutableListOf<Channel<Any>>()
 | 
			
		||||
    private val channelsMutex = Mutex()
 | 
			
		||||
    private val steps = subscope.actor<AccumulatorFlowStep> { step ->
 | 
			
		||||
        when (step) {
 | 
			
		||||
            is DataRetrievedAccumulatorFlowStep -> {
 | 
			
		||||
                if (activeData.first() === step.data) {
 | 
			
		||||
                    dataMutex.withLock {
 | 
			
		||||
                        activeData.removeFirst()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            is SubscribeAccumulatorFlowStep -> channelsMutex.withLock {
 | 
			
		||||
                channelsForBroadcast.add(step.channel)
 | 
			
		||||
                dataMutex.withLock {
 | 
			
		||||
                    val dataToSend = activeData.toList()
 | 
			
		||||
                    safelyWithoutExceptions {
 | 
			
		||||
                        dataToSend.forEach { step.channel.send(it as Any) }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            is UnsubscribeAccumulatorFlowStep -> channelsMutex.withLock {
 | 
			
		||||
                channelsForBroadcast.remove(step.channel)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private val subscriptionJob = sourceDataFlow.subscribeSafelyWithoutExceptions(subscope) {
 | 
			
		||||
        dataMutex.withLock {
 | 
			
		||||
            activeData.addLast(it)
 | 
			
		||||
        }
 | 
			
		||||
        channelsMutex.withLock {
 | 
			
		||||
            channelsForBroadcast.forEach { channel ->
 | 
			
		||||
                safelyWithResult {
 | 
			
		||||
                    channel.send(it as Any)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun collectSafely(collector: FlowCollector<T>) {
 | 
			
		||||
        val channel = Channel<Any>(Channel.UNLIMITED, BufferOverflow.SUSPEND)
 | 
			
		||||
        steps.send(SubscribeAccumulatorFlowStep(channel))
 | 
			
		||||
        for (data in channel) {
 | 
			
		||||
            try {
 | 
			
		||||
                collector.emit(data as T)
 | 
			
		||||
                steps.send(DataRetrievedAccumulatorFlowStep(data))
 | 
			
		||||
            } finally {
 | 
			
		||||
                channel.cancel()
 | 
			
		||||
                steps.send(UnsubscribeAccumulatorFlowStep(channel))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates [AccumulatorFlow] using [this] as base [Flow]
 | 
			
		||||
 */
 | 
			
		||||
fun <T> Flow<T>.accumulatorFlow(scope: CoroutineScope): Flow<T> {
 | 
			
		||||
    return AccumulatorFlow(this, scope)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates [AccumulatorFlow] using [this] with [receiveAsFlow] to get
 | 
			
		||||
 */
 | 
			
		||||
fun <T> Channel<T>.accumulatorFlow(scope: CoroutineScope): Flow<T> {
 | 
			
		||||
    return receiveAsFlow().accumulatorFlow(scope)
 | 
			
		||||
}
 | 
			
		||||
@@ -147,3 +147,10 @@ suspend inline fun <T> runCatchingSafelyWithoutExceptions(
 | 
			
		||||
): Result<T?> = runCatching {
 | 
			
		||||
    safelyWithoutExceptions(onException, block)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun CoroutineScope(
 | 
			
		||||
    context: CoroutineContext,
 | 
			
		||||
    noinline defaultExceptionsHandler: ExceptionHandler<Unit>
 | 
			
		||||
) = CoroutineScope(
 | 
			
		||||
    context + ContextSafelyExceptionHandler(defaultExceptionsHandler)
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
package dev.inmo.micro_utils.crypto
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual fun SourceBytes.md5(): MD5 = CryptoJS.MD5(decodeToString())
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,9 @@ package dev.inmo.micro_utils.crypto
 | 
			
		||||
import java.math.BigInteger
 | 
			
		||||
import java.security.MessageDigest
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @suppress
 | 
			
		||||
 */
 | 
			
		||||
actual fun SourceBytes.md5(): MD5 = BigInteger(
 | 
			
		||||
    1,
 | 
			
		||||
    MessageDigest.getInstance("MD5").digest(this)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,17 +7,16 @@ plugins {
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
    mavenLocal()
 | 
			
		||||
    jcenter()
 | 
			
		||||
    google()
 | 
			
		||||
    mavenCentral()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kotlin {
 | 
			
		||||
    jvm()
 | 
			
		||||
    js(BOTH) {
 | 
			
		||||
        browser()
 | 
			
		||||
        nodejs()
 | 
			
		||||
    }
 | 
			
		||||
//    js(IR) {
 | 
			
		||||
//        browser()
 | 
			
		||||
//        nodejs()
 | 
			
		||||
//    }
 | 
			
		||||
    android {}
 | 
			
		||||
 | 
			
		||||
    sourceSets {
 | 
			
		||||
@@ -30,7 +29,7 @@ kotlin {
 | 
			
		||||
                        it != project
 | 
			
		||||
                        && it.hasProperty("kotlin")
 | 
			
		||||
                        && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
 | 
			
		||||
                        && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
 | 
			
		||||
//                        && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
 | 
			
		||||
                        && it.kotlin.sourceSets.any { it.name.contains("jvmMain") }
 | 
			
		||||
                        && it.kotlin.sourceSets.any { it.name.contains("androidMain") }
 | 
			
		||||
                    ) {
 | 
			
		||||
@@ -39,22 +38,22 @@ kotlin {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        jsMain {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation kotlin('stdlib')
 | 
			
		||||
//        jsMain {
 | 
			
		||||
//            dependencies {
 | 
			
		||||
//                implementation kotlin('stdlib')
 | 
			
		||||
 | 
			
		||||
                project.parent.subprojects.forEach {
 | 
			
		||||
                    if (
 | 
			
		||||
                        it != project
 | 
			
		||||
                        && it.hasProperty("kotlin")
 | 
			
		||||
                        && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
 | 
			
		||||
                        && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
 | 
			
		||||
                    ) {
 | 
			
		||||
                        api it
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
//                project.parent.subprojects.forEach {
 | 
			
		||||
//                    if (
 | 
			
		||||
//                        it != project
 | 
			
		||||
//                        && it.hasProperty("kotlin")
 | 
			
		||||
//                        && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
 | 
			
		||||
//                        && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
 | 
			
		||||
//                    ) {
 | 
			
		||||
//                        api it
 | 
			
		||||
//                    }
 | 
			
		||||
//                }
 | 
			
		||||
//            }
 | 
			
		||||
//        }
 | 
			
		||||
        jvmMain {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation kotlin('stdlib')
 | 
			
		||||
@@ -117,9 +116,9 @@ tasks.dokkaHtml {
 | 
			
		||||
            sourceRoots.setFrom(findSourcesWithName("commonMain"))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        named("jsMain") {
 | 
			
		||||
            sourceRoots.setFrom(findSourcesWithName("jsMain", "commonMain"))
 | 
			
		||||
        }
 | 
			
		||||
//        named("jsMain") {
 | 
			
		||||
//            sourceRoots.setFrom(findSourcesWithName("jsMain", "commonMain"))
 | 
			
		||||
//        }
 | 
			
		||||
 | 
			
		||||
        named("jvmMain") {
 | 
			
		||||
            sourceRoots.setFrom(findSourcesWithName("jvmMain", "commonMain"))
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ kotlin {
 | 
			
		||||
    sourceSets {
 | 
			
		||||
        commonMain {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                api project(":micro_utils.common")
 | 
			
		||||
                api project(":micro_utils.coroutines")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,81 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common
 | 
			
		||||
 | 
			
		||||
import kotlin.reflect.KClass
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Define checkable holder which can be used to precheck that this handler may handle incoming [State]
 | 
			
		||||
 */
 | 
			
		||||
interface CheckableHandlerHolder<I : State, O : State> : StatesHandler<I, O> {
 | 
			
		||||
    suspend fun checkHandleable(state: O): Boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Default realization of [StatesHandler]. It will incapsulate checking of [State] type in [checkHandleable] and class
 | 
			
		||||
 * casting in [handleState]
 | 
			
		||||
 */
 | 
			
		||||
class CustomizableHandlerHolder<I : O, O : State>(
 | 
			
		||||
    private val delegateTo: StatesHandler<I, O>,
 | 
			
		||||
    private val filter: suspend (state: O) -> Boolean
 | 
			
		||||
) : CheckableHandlerHolder<I, O> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks that [state] can be handled by [delegateTo]. Under the hood it will check exact equality of [state]
 | 
			
		||||
     * [KClass] and use [KClass.isInstance] of [inputKlass] if [strict] == false
 | 
			
		||||
     */
 | 
			
		||||
    override suspend fun checkHandleable(state: O) = filter(state)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calls [delegateTo] method [StatesHandler.handleState] with [state] casted to [I]. Use [checkHandleable]
 | 
			
		||||
     * to be sure that this [StatesHandlerHolder] will be able to handle [state]
 | 
			
		||||
     */
 | 
			
		||||
    override suspend fun StatesMachine<in O>.handleState(state: I): O? {
 | 
			
		||||
        return delegateTo.run { handleState(state) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun <I : O, O : State> CheckableHandlerHolder(
 | 
			
		||||
    inputKlass: KClass<I>,
 | 
			
		||||
    strict: Boolean = false,
 | 
			
		||||
    delegateTo: StatesHandler<I, O>
 | 
			
		||||
) = CustomizableHandlerHolder(
 | 
			
		||||
    StatesHandler<O, O> {
 | 
			
		||||
        delegateTo.run { handleState(it as I) }
 | 
			
		||||
    },
 | 
			
		||||
    if (strict) {
 | 
			
		||||
        { it::class == inputKlass }
 | 
			
		||||
    } else {
 | 
			
		||||
        { inputKlass.isInstance(it) }
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Deprecated("Renamed", ReplaceWith("CheckableHandlerHolder"))
 | 
			
		||||
fun <I : O, O : State> StateHandlerHolder(
 | 
			
		||||
    inputKlass: KClass<I>,
 | 
			
		||||
    strict: Boolean = false,
 | 
			
		||||
    delegateTo: StatesHandler<I, O>
 | 
			
		||||
) = CheckableHandlerHolder(inputKlass, strict, delegateTo)
 | 
			
		||||
 | 
			
		||||
inline fun <reified I : O, O : State> CheckableHandlerHolder(
 | 
			
		||||
    strict: Boolean = false,
 | 
			
		||||
    delegateTo: StatesHandler<I, O>
 | 
			
		||||
) = CheckableHandlerHolder(I::class, strict, delegateTo)
 | 
			
		||||
 | 
			
		||||
@Deprecated("Renamed", ReplaceWith("CheckableHandlerHolder"))
 | 
			
		||||
inline fun <reified I : O, O : State> StateHandlerHolder(
 | 
			
		||||
    strict: Boolean = false,
 | 
			
		||||
    delegateTo: StatesHandler<I, O>
 | 
			
		||||
) = CheckableHandlerHolder(strict, delegateTo)
 | 
			
		||||
 | 
			
		||||
inline fun <reified I : O, O: State> StatesHandler<I, O>.holder(
 | 
			
		||||
    strict: Boolean = true
 | 
			
		||||
) = CheckableHandlerHolder<I, O>(
 | 
			
		||||
    I::class,
 | 
			
		||||
    strict,
 | 
			
		||||
    this
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
inline fun <I : O, O: State> StatesHandler<I, O>.holder(
 | 
			
		||||
    noinline filter: suspend (state: State) -> Boolean
 | 
			
		||||
) = CustomizableHandlerHolder<O, O>(
 | 
			
		||||
    { this@holder.run { handleState(it as I) } },
 | 
			
		||||
    filter
 | 
			
		||||
)
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common
 | 
			
		||||
 | 
			
		||||
import kotlin.reflect.KClass
 | 
			
		||||
 | 
			
		||||
class StateHandlerHolder<I : State>(
 | 
			
		||||
    private val inputKlass: KClass<I>,
 | 
			
		||||
    private val strict: Boolean = false,
 | 
			
		||||
    private val delegateTo: StatesHandler<I>
 | 
			
		||||
) : StatesHandler<State> {
 | 
			
		||||
    fun checkHandleable(state: State) = state::class == inputKlass || (!strict && inputKlass.isInstance(state))
 | 
			
		||||
 | 
			
		||||
    override suspend fun StatesMachine.handleState(state: State): State? {
 | 
			
		||||
        return delegateTo.run { handleState(state as I) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common
 | 
			
		||||
 | 
			
		||||
fun interface StatesHandler<I : State> {
 | 
			
		||||
    suspend fun StatesMachine.handleState(state: I): State?
 | 
			
		||||
/**
 | 
			
		||||
 * Default realization of states handler
 | 
			
		||||
 */
 | 
			
		||||
fun interface StatesHandler<I : State, O: State> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Main handling of [state]. In case when this [state] leads to another [State] and [handleState] returns not null
 | 
			
		||||
     * [State] it is assumed that chain is not completed.
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun StatesMachine<in O>.handleState(state: I): O?
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,70 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.common.Optional
 | 
			
		||||
import dev.inmo.micro_utils.common.onPresented
 | 
			
		||||
import dev.inmo.micro_utils.coroutines.*
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import kotlinx.coroutines.flow.asFlow
 | 
			
		||||
import kotlinx.coroutines.sync.Mutex
 | 
			
		||||
import kotlinx.coroutines.sync.withLock
 | 
			
		||||
 | 
			
		||||
private suspend fun <I : State> StatesMachine.launchStateHandling(
 | 
			
		||||
    state: State,
 | 
			
		||||
    handlers: List<StateHandlerHolder<out I>>
 | 
			
		||||
): State? {
 | 
			
		||||
/**
 | 
			
		||||
 * Default [StatesMachine] may [startChain] and use inside logic for handling [State]s. By default you may use
 | 
			
		||||
 * [DefaultStatesMachine] or build it with [dev.inmo.micro_utils.fsm.common.dsl.buildFSM]. Implementers MUST NOT start
 | 
			
		||||
 * handling until [start] method will be called
 | 
			
		||||
 */
 | 
			
		||||
interface StatesMachine<T : State> : StatesHandler<T, T> {
 | 
			
		||||
    suspend fun launchStateHandling(
 | 
			
		||||
        state: T,
 | 
			
		||||
        handlers: List<CheckableHandlerHolder<in T, T>>
 | 
			
		||||
    ): T? {
 | 
			
		||||
        return handlers.firstOrNull { it.checkHandleable(state) } ?.run {
 | 
			
		||||
            handleState(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
class StatesMachine (
 | 
			
		||||
    private val statesManager: StatesManager,
 | 
			
		||||
    private val handlers: List<StateHandlerHolder<*>>
 | 
			
		||||
) : StatesHandler<State> {
 | 
			
		||||
    override suspend fun StatesMachine.handleState(state: State): State? = launchStateHandling(state, handlers)
 | 
			
		||||
    /**
 | 
			
		||||
     * Starts handling of [State]s
 | 
			
		||||
     */
 | 
			
		||||
    fun start(scope: CoroutineScope): Job
 | 
			
		||||
 | 
			
		||||
    fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions {
 | 
			
		||||
        val statePerformer: suspend (State) -> Unit = { state: State ->
 | 
			
		||||
    /**
 | 
			
		||||
     * Start chain of [State]s witn [state]
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun startChain(state: T)
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        /**
 | 
			
		||||
         * Creates [DefaultStatesMachine]
 | 
			
		||||
         */
 | 
			
		||||
        operator fun <T: State> invoke(
 | 
			
		||||
            statesManager: StatesManager<T>,
 | 
			
		||||
            handlers: List<CheckableHandlerHolder<in T, T>>
 | 
			
		||||
        ) = DefaultStatesMachine(statesManager, handlers)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Default realization of [StatesMachine]. It uses [statesManager] for incapsulation of [State]s storing and contexts
 | 
			
		||||
 * resolving, and uses [launchStateHandling] for [State] handling.
 | 
			
		||||
 *
 | 
			
		||||
 * This class suppose to be extended in case you wish some custom behaviour inside of [launchStateHandling], for example
 | 
			
		||||
 */
 | 
			
		||||
open class DefaultStatesMachine <T: State>(
 | 
			
		||||
    protected val statesManager: StatesManager<T>,
 | 
			
		||||
    protected val handlers: List<CheckableHandlerHolder<in T, T>>,
 | 
			
		||||
) : StatesMachine<T> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Will call [launchStateHandling] for state handling
 | 
			
		||||
     */
 | 
			
		||||
    override suspend fun StatesMachine<in T>.handleState(state: T): T? = launchStateHandling(state, handlers)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This
 | 
			
		||||
     */
 | 
			
		||||
    protected val statesJobs = mutableMapOf<T, Job>()
 | 
			
		||||
    protected val statesJobsMutex = Mutex()
 | 
			
		||||
 | 
			
		||||
    protected open suspend fun performUpdate(state: T) {
 | 
			
		||||
        val newState = launchStateHandling(state, handlers)
 | 
			
		||||
        if (newState != null) {
 | 
			
		||||
            statesManager.update(state, newState)
 | 
			
		||||
@@ -28,19 +72,49 @@ class StatesMachine (
 | 
			
		||||
            statesManager.endChain(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun performStateUpdate(previousState: Optional<T>, actualState: T, scope: CoroutineScope) {
 | 
			
		||||
        statesJobsMutex.withLock {
 | 
			
		||||
            statesJobs[actualState] ?.cancel()
 | 
			
		||||
            statesJobs[actualState] = scope.launch {
 | 
			
		||||
                performUpdate(actualState)
 | 
			
		||||
            }.also { job ->
 | 
			
		||||
                job.invokeOnCompletion { _ ->
 | 
			
		||||
                    scope.launch {
 | 
			
		||||
                        statesJobsMutex.withLock {
 | 
			
		||||
                            if (statesJobs[actualState] == job) {
 | 
			
		||||
                                statesJobs.remove(actualState)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Launch handling of states. On [statesManager] [StatesManager.onStartChain],
 | 
			
		||||
     * [statesManager] [StatesManager.onChainStateUpdated] will be called lambda with performing of state. If
 | 
			
		||||
     * [launchStateHandling] will returns some [State] then [statesManager] [StatesManager.update] will be used, otherwise
 | 
			
		||||
     * [StatesManager.endChain].
 | 
			
		||||
     */
 | 
			
		||||
    override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions {
 | 
			
		||||
        statesManager.onStartChain.subscribeSafelyWithoutExceptions(this) {
 | 
			
		||||
            launch { statePerformer(it) }
 | 
			
		||||
            launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) }
 | 
			
		||||
        }
 | 
			
		||||
        statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(this) {
 | 
			
		||||
            launch { statePerformer(it.second) }
 | 
			
		||||
            launch { performStateUpdate(Optional.presented(it.first), it.second, scope.LinkedSupervisorScope()) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        statesManager.getActiveStates().forEach {
 | 
			
		||||
            launch { statePerformer(it) }
 | 
			
		||||
            launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun startChain(state: State) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Just calls [StatesManager.startChain] of [statesManager]
 | 
			
		||||
     */
 | 
			
		||||
    override suspend fun startChain(state: T) {
 | 
			
		||||
        statesManager.startChain(state)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,92 +1,30 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common
 | 
			
		||||
 | 
			
		||||
import kotlinx.coroutines.flow.*
 | 
			
		||||
import kotlinx.coroutines.sync.Mutex
 | 
			
		||||
import kotlinx.coroutines.sync.withLock
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
 | 
			
		||||
interface StatesManager {
 | 
			
		||||
    val onChainStateUpdated: Flow<Pair<State, State>>
 | 
			
		||||
    val onStartChain: Flow<State>
 | 
			
		||||
    val onEndChain: Flow<State>
 | 
			
		||||
interface StatesManager<T : State> {
 | 
			
		||||
    val onChainStateUpdated: Flow<Pair<T, T>>
 | 
			
		||||
    val onStartChain: Flow<T>
 | 
			
		||||
    val onEndChain: Flow<T>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Must set current set using [State.context]
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun update(old: State, new: State)
 | 
			
		||||
    suspend fun update(old: T, new: T)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Starts chain with [state] as first [State]. May returns false in case of [State.context] of [state] is already
 | 
			
		||||
     * busy by the other [State]
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun startChain(state: State)
 | 
			
		||||
    suspend fun startChain(state: T)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Ends chain with context from [state]. In case when [State.context] of [state] is absent, [state] should be just
 | 
			
		||||
     * ignored
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun endChain(state: State)
 | 
			
		||||
    suspend fun endChain(state: T)
 | 
			
		||||
 | 
			
		||||
    suspend fun getActiveStates(): List<State>
 | 
			
		||||
    suspend fun getActiveStates(): List<T>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param onContextsConflictResolver Receive old [State], new one and the state currently placed on new [State.context]
 | 
			
		||||
 * key. In case when this callback will returns true, the state placed on [State.context] of new will be replaced by
 | 
			
		||||
 * new state by using [endChain] with that state
 | 
			
		||||
 */
 | 
			
		||||
class InMemoryStatesManager(
 | 
			
		||||
    private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true }
 | 
			
		||||
) : StatesManager {
 | 
			
		||||
    private val _onChainStateUpdated = MutableSharedFlow<Pair<State, State>>(0)
 | 
			
		||||
    override val onChainStateUpdated: Flow<Pair<State, State>> = _onChainStateUpdated.asSharedFlow()
 | 
			
		||||
    private val _onStartChain = MutableSharedFlow<State>(0)
 | 
			
		||||
    override val onStartChain: Flow<State> = _onStartChain.asSharedFlow()
 | 
			
		||||
    private val _onEndChain = MutableSharedFlow<State>(0)
 | 
			
		||||
    override val onEndChain: Flow<State> = _onEndChain.asSharedFlow()
 | 
			
		||||
 | 
			
		||||
    private val contextsToStates = mutableMapOf<Any, State>()
 | 
			
		||||
    private val mapMutex = Mutex()
 | 
			
		||||
 | 
			
		||||
    override suspend fun update(old: State, new: State) = mapMutex.withLock {
 | 
			
		||||
        when {
 | 
			
		||||
            contextsToStates[old.context] != old -> return@withLock
 | 
			
		||||
            old.context == new.context || !contextsToStates.containsKey(new.context) -> {
 | 
			
		||||
                contextsToStates[old.context] = new
 | 
			
		||||
                _onChainStateUpdated.emit(old to new)
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                val stateOnNewOneContext = contextsToStates.getValue(new.context)
 | 
			
		||||
                if (onContextsConflictResolver(old, new, stateOnNewOneContext)) {
 | 
			
		||||
                    endChainWithoutLock(stateOnNewOneContext)
 | 
			
		||||
                    contextsToStates.remove(old.context)
 | 
			
		||||
                    contextsToStates[new.context] = new
 | 
			
		||||
                    _onChainStateUpdated.emit(old to new)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun startChain(state: State) = mapMutex.withLock {
 | 
			
		||||
        if (!contextsToStates.containsKey(state.context)) {
 | 
			
		||||
            contextsToStates[state.context] = state
 | 
			
		||||
            _onStartChain.emit(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun endChainWithoutLock(state: State) {
 | 
			
		||||
        if (contextsToStates[state.context] == state) {
 | 
			
		||||
            contextsToStates.remove(state.context)
 | 
			
		||||
            _onEndChain.emit(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun endChain(state: State) {
 | 
			
		||||
        mapMutex.withLock {
 | 
			
		||||
            endChainWithoutLock(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getActiveStates(): List<State> = contextsToStates.values.toList()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,60 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.common.*
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import kotlinx.coroutines.sync.withLock
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This extender of [StatesMachine] interface declare one new function [updateChain]. Realizations of this interface
 | 
			
		||||
 * must be able to perform update of chain in internal [StatesManager]
 | 
			
		||||
 */
 | 
			
		||||
interface UpdatableStatesMachine<T : State> : StatesMachine<T> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Update chain with current state equal to [currentState] with [newState]. Behaviour of this update preforming
 | 
			
		||||
     * in cases when [currentState] does not exist in [StatesManager] must be declared inside of realization of
 | 
			
		||||
     * [StatesManager.update] function
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun updateChain(currentState: T, newState: T)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
open class DefaultUpdatableStatesMachine<T : State>(
 | 
			
		||||
    statesManager: StatesManager<T>,
 | 
			
		||||
    handlers: List<CheckableHandlerHolder<in T, T>>,
 | 
			
		||||
) : DefaultStatesMachine<T>(
 | 
			
		||||
    statesManager,
 | 
			
		||||
    handlers
 | 
			
		||||
), UpdatableStatesMachine<T> {
 | 
			
		||||
    protected val jobsStates = mutableMapOf<Job, T>()
 | 
			
		||||
 | 
			
		||||
    override suspend fun performStateUpdate(previousState: Optional<T>, actualState: T, scope: CoroutineScope) {
 | 
			
		||||
        statesJobsMutex.withLock {
 | 
			
		||||
            if (compare(previousState, actualState)) {
 | 
			
		||||
                statesJobs[actualState] ?.cancel()
 | 
			
		||||
            }
 | 
			
		||||
            val job = previousState.mapOnPresented {
 | 
			
		||||
                statesJobs.remove(it)
 | 
			
		||||
            } ?.takeIf { it.isActive } ?: scope.launch {
 | 
			
		||||
                performUpdate(actualState)
 | 
			
		||||
            }.also { job ->
 | 
			
		||||
                job.invokeOnCompletion { _ ->
 | 
			
		||||
                    scope.launch {
 | 
			
		||||
                        statesJobsMutex.withLock {
 | 
			
		||||
                            statesJobs.remove(
 | 
			
		||||
                                jobsStates[job] ?: return@withLock
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            jobsStates.remove(job)
 | 
			
		||||
            statesJobs[actualState] = job
 | 
			
		||||
            jobsStates[job] = actualState
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected open suspend fun compare(previous: Optional<T>, new: T): Boolean = previous.dataOrNull() != new
 | 
			
		||||
 | 
			
		||||
    override suspend fun updateChain(currentState: T, newState: T) {
 | 
			
		||||
        statesManager.update(currentState, newState)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +1,61 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common.dsl
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.*
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
 | 
			
		||||
import kotlin.reflect.KClass
 | 
			
		||||
 | 
			
		||||
class FSMBuilder(
 | 
			
		||||
    var statesManager: StatesManager = InMemoryStatesManager()
 | 
			
		||||
) {
 | 
			
		||||
    private var states = mutableListOf<StateHandlerHolder<*>>()
 | 
			
		||||
 | 
			
		||||
    fun <I : State> add(kClass: KClass<I>, handler: StatesHandler<I>) {
 | 
			
		||||
        states.add(StateHandlerHolder(kClass, false, handler))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun <I : State> addStrict(kClass: KClass<I>, handler: StatesHandler<I>) {
 | 
			
		||||
        states.add(StateHandlerHolder(kClass, true, handler))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun build() = StatesMachine(
 | 
			
		||||
class FSMBuilder<T : State>(
 | 
			
		||||
    var statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
 | 
			
		||||
    val fsmBuilder: (statesManager: StatesManager<T>, states: List<CheckableHandlerHolder<T, T>>) -> StatesMachine<T> = { statesManager, states ->
 | 
			
		||||
        StatesMachine(
 | 
			
		||||
            statesManager,
 | 
			
		||||
        states.toList()
 | 
			
		||||
            states
 | 
			
		||||
        )
 | 
			
		||||
    },
 | 
			
		||||
    var defaultStateHandler: StatesHandler<T, T>? = StatesHandler { null }
 | 
			
		||||
) {
 | 
			
		||||
    private var states = mutableListOf<CheckableHandlerHolder<T, T>>()
 | 
			
		||||
 | 
			
		||||
    fun add(handler: CheckableHandlerHolder<T, T>) {
 | 
			
		||||
        states.add(handler)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
inline fun <reified I : State> FSMBuilder.onStateOrSubstate(handler: StatesHandler<I>) {
 | 
			
		||||
    fun <I : T> add(kClass: KClass<I>, handler: StatesHandler<I, T>) {
 | 
			
		||||
        add(CheckableHandlerHolder(kClass, false, handler))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun <I : T> add(filter: suspend (state: State) -> Boolean, handler: StatesHandler<I, T>) {
 | 
			
		||||
        add(handler.holder(filter))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun <I : T> addStrict(kClass: KClass<I>, handler: StatesHandler<I, T>) {
 | 
			
		||||
        states.add(CheckableHandlerHolder(kClass, true, handler))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline fun <reified I : T> onStateOrSubstate(handler: StatesHandler<I, T>) {
 | 
			
		||||
        add(I::class, handler)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
inline fun <reified I : State> FSMBuilder.strictlyOn(handler: StatesHandler<I>) {
 | 
			
		||||
    inline fun <reified I : T> strictlyOn(handler: StatesHandler<I, T>) {
 | 
			
		||||
        addStrict(I::class, handler)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
fun buildFSM(
 | 
			
		||||
    block: FSMBuilder.() -> Unit
 | 
			
		||||
): StatesMachine = FSMBuilder().apply(block).build()
 | 
			
		||||
    inline fun <reified I : T> doWhen(
 | 
			
		||||
        noinline filter: suspend (state: State) -> Boolean,
 | 
			
		||||
        handler: StatesHandler<I, T>
 | 
			
		||||
    ) {
 | 
			
		||||
        add(filter, handler)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun build() = fsmBuilder(
 | 
			
		||||
        statesManager,
 | 
			
		||||
        states.toList().let { list ->
 | 
			
		||||
            defaultStateHandler ?.let { list + it.holder { true } } ?: list
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun <T : State> buildFSM(
 | 
			
		||||
    block: FSMBuilder<T>.() -> Unit
 | 
			
		||||
): StatesMachine<T> = FSMBuilder<T>().apply(block).build()
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,101 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common.managers
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.State
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.StatesManager
 | 
			
		||||
import kotlinx.coroutines.flow.*
 | 
			
		||||
import kotlinx.coroutines.sync.Mutex
 | 
			
		||||
import kotlinx.coroutines.sync.withLock
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implement this repo if you want to use some custom repo for [DefaultStatesManager]
 | 
			
		||||
 */
 | 
			
		||||
interface DefaultStatesManagerRepo<T : State> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Must save [state] as current state of chain with [State.context] of [state]
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun set(state: T)
 | 
			
		||||
    /**
 | 
			
		||||
     * Remove exactly [state]. In case if internally [State.context] is busy with different [State], that [State] should
 | 
			
		||||
     * NOT be removed
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun removeState(state: T)
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Current list of available and saved states
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun getStates(): List<T>
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Current state by [context]
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun getContextState(context: Any): T?
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Current state by [context]
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun contains(context: Any): Boolean = getContextState(context) != null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param repo This repo will be used as repository for storing states. All operations with this repo will happen BEFORE
 | 
			
		||||
 * any event will be sent to [onChainStateUpdated], [onStartChain] or [onEndChain]. By default will be used
 | 
			
		||||
 * [InMemoryDefaultStatesManagerRepo] or you may create custom [DefaultStatesManagerRepo] and pass as [repo] parameter
 | 
			
		||||
 * @param onContextsConflictResolver Receive old [State], new one and the state currently placed on new [State.context]
 | 
			
		||||
 * key. In case when this callback will returns true, the state placed on [State.context] of new will be replaced by
 | 
			
		||||
 * new state by using [endChain] with that state
 | 
			
		||||
 */
 | 
			
		||||
class DefaultStatesManager<T : State>(
 | 
			
		||||
    private val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(),
 | 
			
		||||
    private val onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
 | 
			
		||||
) : StatesManager<T> {
 | 
			
		||||
    private val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0)
 | 
			
		||||
    override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow()
 | 
			
		||||
    private val _onStartChain = MutableSharedFlow<T>(0)
 | 
			
		||||
    override val onStartChain: Flow<T> = _onStartChain.asSharedFlow()
 | 
			
		||||
    private val _onEndChain = MutableSharedFlow<T>(0)
 | 
			
		||||
    override val onEndChain: Flow<T> = _onEndChain.asSharedFlow()
 | 
			
		||||
 | 
			
		||||
    private val mapMutex = Mutex()
 | 
			
		||||
 | 
			
		||||
    override suspend fun update(old: T, new: T) = mapMutex.withLock {
 | 
			
		||||
        val stateByOldContext: T? = repo.getContextState(old.context)
 | 
			
		||||
        when {
 | 
			
		||||
            stateByOldContext != old -> return@withLock
 | 
			
		||||
            stateByOldContext == null || old.context == new.context -> {
 | 
			
		||||
                repo.set(new)
 | 
			
		||||
                _onChainStateUpdated.emit(old to new)
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                val stateOnNewOneContext = repo.getContextState(new.context)
 | 
			
		||||
                if (stateOnNewOneContext == null || onContextsConflictResolver(old, new, stateOnNewOneContext)) {
 | 
			
		||||
                    stateOnNewOneContext ?.let { endChainWithoutLock(it) }
 | 
			
		||||
                    repo.removeState(old)
 | 
			
		||||
                    repo.set(new)
 | 
			
		||||
                    _onChainStateUpdated.emit(old to new)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun startChain(state: T) = mapMutex.withLock {
 | 
			
		||||
        if (!repo.contains(state.context)) {
 | 
			
		||||
            repo.set(state)
 | 
			
		||||
            _onStartChain.emit(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun endChainWithoutLock(state: T) {
 | 
			
		||||
        if (repo.getContextState(state.context) == state) {
 | 
			
		||||
            repo.removeState(state)
 | 
			
		||||
            _onEndChain.emit(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun endChain(state: T) {
 | 
			
		||||
        mapMutex.withLock {
 | 
			
		||||
            endChainWithoutLock(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getActiveStates(): List<T> = repo.getStates()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common.managers
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.State
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Simple [DefaultStatesManagerRepo] for [DefaultStatesManager] which will store data in [map] and use primitive
 | 
			
		||||
 * functionality
 | 
			
		||||
 */
 | 
			
		||||
class InMemoryDefaultStatesManagerRepo<T : State>(
 | 
			
		||||
    private val map: MutableMap<Any, T> = mutableMapOf()
 | 
			
		||||
) : DefaultStatesManagerRepo<T> {
 | 
			
		||||
    override suspend fun set(state: T) {
 | 
			
		||||
        map[state.context] = state
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun removeState(state: T) {
 | 
			
		||||
        map.remove(state.context)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getStates(): List<T> = map.values.toList()
 | 
			
		||||
 | 
			
		||||
    override suspend fun getContextState(context: Any): T? = map[context]
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Any): Boolean = map.contains(context)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.common.managers
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.State
 | 
			
		||||
import kotlinx.coroutines.flow.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates [DefaultStatesManager] with [InMemoryDefaultStatesManagerRepo]
 | 
			
		||||
 *
 | 
			
		||||
 * @param onContextsConflictResolver Receive old [State], new one and the state currently placed on new [State.context]
 | 
			
		||||
 * key. In case when this callback will returns true, the state placed on [State.context] of new will be replaced by
 | 
			
		||||
 * new state by using [endChain] with that state
 | 
			
		||||
 */
 | 
			
		||||
@Deprecated("Use DefaultStatesManager instead", ReplaceWith("DefaultStatesManager"))
 | 
			
		||||
fun <T: State> InMemoryStatesManager(
 | 
			
		||||
    onContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
 | 
			
		||||
) = DefaultStatesManager(onContextsConflictResolver = onContextsConflictResolver)
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.*
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.dsl.buildFSM
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.dsl.strictlyOn
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.managers.InMemoryStatesManager
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
 | 
			
		||||
sealed interface TrafficLightState : State {
 | 
			
		||||
@@ -25,9 +26,9 @@ class PlayableMain {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val statesManager = InMemoryStatesManager()
 | 
			
		||||
            val statesManager = DefaultStatesManager<TrafficLightState>()
 | 
			
		||||
 | 
			
		||||
            val machine = buildFSM {
 | 
			
		||||
            val machine = buildFSM<TrafficLightState> {
 | 
			
		||||
                strictlyOn<GreenCommon> {
 | 
			
		||||
                    delay(1000L)
 | 
			
		||||
                    YellowCommon(it.context).also(::println)
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.repos.common
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.State
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
 | 
			
		||||
import dev.inmo.micro_utils.repos.*
 | 
			
		||||
import dev.inmo.micro_utils.repos.pagination.getAll
 | 
			
		||||
 | 
			
		||||
class KeyValueBasedDefaultStatesManagerRepo<T : State>(
 | 
			
		||||
    private val keyValueRepo: KeyValueRepo<Any, T>
 | 
			
		||||
) : DefaultStatesManagerRepo<T> {
 | 
			
		||||
    override suspend fun set(state: T) {
 | 
			
		||||
        keyValueRepo.set(state.context, state)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun removeState(state: T) {
 | 
			
		||||
        if (keyValueRepo.get(state.context) == state) {
 | 
			
		||||
            keyValueRepo.unset(state.context)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getStates(): List<T> = keyValueRepo.getAll { keys(it) }.map { it.second }
 | 
			
		||||
    override suspend fun getContextState(context: Any): T? = keyValueRepo.get(context)
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Any): Boolean = keyValueRepo.contains(context)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,83 +0,0 @@
 | 
			
		||||
package dev.inmo.micro_utils.fsm.repos.common
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.State
 | 
			
		||||
import dev.inmo.micro_utils.fsm.common.StatesManager
 | 
			
		||||
import dev.inmo.micro_utils.repos.*
 | 
			
		||||
import dev.inmo.micro_utils.repos.mappers.withMapper
 | 
			
		||||
import dev.inmo.micro_utils.repos.pagination.getAll
 | 
			
		||||
import kotlinx.coroutines.flow.*
 | 
			
		||||
import kotlinx.coroutines.sync.Mutex
 | 
			
		||||
import kotlinx.coroutines.sync.withLock
 | 
			
		||||
 | 
			
		||||
class KeyValueBasedStatesManager(
 | 
			
		||||
    private val keyValueRepo: KeyValueRepo<Any, State>,
 | 
			
		||||
    private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true }
 | 
			
		||||
) : StatesManager {
 | 
			
		||||
    private val _onChainStateUpdated = MutableSharedFlow<Pair<State, State>>(0)
 | 
			
		||||
    override val onChainStateUpdated: Flow<Pair<State, State>> = _onChainStateUpdated.asSharedFlow()
 | 
			
		||||
    private val _onEndChain = MutableSharedFlow<State>(0)
 | 
			
		||||
    override val onEndChain: Flow<State> = _onEndChain.asSharedFlow()
 | 
			
		||||
 | 
			
		||||
    override val onStartChain: Flow<State> = keyValueRepo.onNewValue.map { it.second }
 | 
			
		||||
 | 
			
		||||
    private val mutex = Mutex()
 | 
			
		||||
 | 
			
		||||
    override suspend fun update(old: State, new: State) {
 | 
			
		||||
        mutex.withLock {
 | 
			
		||||
            when {
 | 
			
		||||
                keyValueRepo.get(old.context) != old -> return@withLock
 | 
			
		||||
                old.context == new.context || !keyValueRepo.contains(new.context) -> {
 | 
			
		||||
                    keyValueRepo.set(old.context, new)
 | 
			
		||||
                    _onChainStateUpdated.emit(old to new)
 | 
			
		||||
                }
 | 
			
		||||
                else -> {
 | 
			
		||||
                    val stateOnNewOneContext = keyValueRepo.get(new.context)!!
 | 
			
		||||
                    if (onContextsConflictResolver(old, new, stateOnNewOneContext)) {
 | 
			
		||||
                        endChainWithoutLock(stateOnNewOneContext)
 | 
			
		||||
                        keyValueRepo.unset(old.context)
 | 
			
		||||
                        keyValueRepo.set(new.context, new)
 | 
			
		||||
                        _onChainStateUpdated.emit(old to new)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun startChain(state: State) {
 | 
			
		||||
        if (!keyValueRepo.contains(state.context)) {
 | 
			
		||||
            keyValueRepo.set(state.context, state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun endChainWithoutLock(state: State) {
 | 
			
		||||
        if (keyValueRepo.get(state.context) == state) {
 | 
			
		||||
            keyValueRepo.unset(state.context)
 | 
			
		||||
            _onEndChain.emit(state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun endChain(state: State) {
 | 
			
		||||
        mutex.withLock { endChainWithoutLock(state) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getActiveStates(): List<State> {
 | 
			
		||||
        return keyValueRepo.getAll { keys(it) }.map { it.second }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <reified TargetContextType, reified TargetStateType> createStatesManager(
 | 
			
		||||
    targetKeyValueRepo: KeyValueRepo<TargetContextType, TargetStateType>,
 | 
			
		||||
    noinline contextToOutTransformer: suspend Any.() -> TargetContextType,
 | 
			
		||||
    noinline stateToOutTransformer: suspend State.() -> TargetStateType,
 | 
			
		||||
    noinline outToContextTransformer: suspend TargetContextType.() -> Any,
 | 
			
		||||
    noinline outToStateTransformer: suspend TargetStateType.() -> State,
 | 
			
		||||
) = KeyValueBasedStatesManager(
 | 
			
		||||
    targetKeyValueRepo.withMapper<Any, State, TargetContextType, TargetStateType>(
 | 
			
		||||
        contextToOutTransformer,
 | 
			
		||||
        stateToOutTransformer,
 | 
			
		||||
        outToContextTransformer,
 | 
			
		||||
        outToStateTransformer
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
@@ -7,43 +7,43 @@ android.useAndroidX=true
 | 
			
		||||
android.enableJetifier=true
 | 
			
		||||
org.gradle.jvmargs=-Xmx2g
 | 
			
		||||
 | 
			
		||||
kotlin_version=1.5.20
 | 
			
		||||
kotlin_coroutines_version=1.5.0
 | 
			
		||||
kotlin_serialisation_core_version=1.2.1
 | 
			
		||||
kotlin_exposed_version=0.32.1
 | 
			
		||||
kotlin_version=1.6.10
 | 
			
		||||
kotlin_coroutines_version=1.6.0
 | 
			
		||||
kotlin_serialisation_core_version=1.3.2
 | 
			
		||||
kotlin_exposed_version=0.37.2
 | 
			
		||||
 | 
			
		||||
ktor_version=1.6.0
 | 
			
		||||
ktor_version=1.6.7
 | 
			
		||||
 | 
			
		||||
klockVersion=2.1.2
 | 
			
		||||
klockVersion=2.4.10
 | 
			
		||||
 | 
			
		||||
github_release_plugin_version=2.2.12
 | 
			
		||||
 | 
			
		||||
uuidVersion=0.3.0
 | 
			
		||||
uuidVersion=0.3.1
 | 
			
		||||
 | 
			
		||||
# ANDROID
 | 
			
		||||
 | 
			
		||||
core_ktx_version=1.5.0
 | 
			
		||||
core_ktx_version=1.7.0
 | 
			
		||||
androidx_recycler_version=1.2.1
 | 
			
		||||
appcompat_version=1.3.0
 | 
			
		||||
appcompat_version=1.4.0
 | 
			
		||||
 | 
			
		||||
android_minSdkVersion=19
 | 
			
		||||
android_compileSdkVersion=30
 | 
			
		||||
android_buildToolsVersion=30.0.3
 | 
			
		||||
dexcount_version=2.0.0
 | 
			
		||||
android_compileSdkVersion=32
 | 
			
		||||
android_buildToolsVersion=32.0.0
 | 
			
		||||
dexcount_version=3.0.1
 | 
			
		||||
junit_version=4.12
 | 
			
		||||
test_ext_junit_version=1.1.2
 | 
			
		||||
espresso_core=3.3.0
 | 
			
		||||
 | 
			
		||||
# JS NPM
 | 
			
		||||
 | 
			
		||||
crypto_js_version=4.0.0
 | 
			
		||||
crypto_js_version=4.1.1
 | 
			
		||||
 | 
			
		||||
# Dokka
 | 
			
		||||
 | 
			
		||||
dokka_version=1.4.32
 | 
			
		||||
dokka_version=1.6.0
 | 
			
		||||
 | 
			
		||||
# Project data
 | 
			
		||||
 | 
			
		||||
group=dev.inmo
 | 
			
		||||
version=0.5.15
 | 
			
		||||
android_code_version=56
 | 
			
		||||
version=0.9.1
 | 
			
		||||
android_code_version=91
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
distributionBase=GRADLE_USER_HOME
 | 
			
		||||
distributionPath=wrapper/dists
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
 | 
			
		||||
zipStoreBase=GRADLE_USER_HOME
 | 
			
		||||
zipStorePath=wrapper/dists
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
package dev.inmo.micro_utils.ktor.client
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.common.MPPFile
 | 
			
		||||
import io.ktor.client.request.forms.InputProvider
 | 
			
		||||
 | 
			
		||||
expect suspend fun MPPFile.inputProvider(): InputProvider
 | 
			
		||||
@@ -1,16 +1,20 @@
 | 
			
		||||
package dev.inmo.micro_utils.ktor.client
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.common.MPPFile
 | 
			
		||||
import dev.inmo.micro_utils.common.filename
 | 
			
		||||
import dev.inmo.micro_utils.ktor.common.*
 | 
			
		||||
import io.ktor.client.HttpClient
 | 
			
		||||
import io.ktor.client.request.get
 | 
			
		||||
import io.ktor.client.request.post
 | 
			
		||||
import io.ktor.client.request.*
 | 
			
		||||
import io.ktor.client.request.forms.*
 | 
			
		||||
import io.ktor.http.*
 | 
			
		||||
import io.ktor.utils.io.core.ByteReadPacket
 | 
			
		||||
import kotlinx.serialization.*
 | 
			
		||||
 | 
			
		||||
typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
 | 
			
		||||
 | 
			
		||||
class UnifiedRequester(
 | 
			
		||||
    private val client: HttpClient = HttpClient(),
 | 
			
		||||
    private val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
 | 
			
		||||
    val client: HttpClient = HttpClient(),
 | 
			
		||||
    val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
 | 
			
		||||
) {
 | 
			
		||||
    suspend fun <ResultType> uniget(
 | 
			
		||||
        url: String,
 | 
			
		||||
@@ -31,6 +35,54 @@ class UnifiedRequester(
 | 
			
		||||
        resultDeserializer: DeserializationStrategy<ResultType>
 | 
			
		||||
    ) = client.unipost(url, bodyInfo, resultDeserializer, serialFormat)
 | 
			
		||||
 | 
			
		||||
    suspend fun <ResultType> unimultipart(
 | 
			
		||||
        url: String,
 | 
			
		||||
        filename: String,
 | 
			
		||||
        inputProvider: InputProvider,
 | 
			
		||||
        resultDeserializer: DeserializationStrategy<ResultType>,
 | 
			
		||||
        mimetype: String = "*/*",
 | 
			
		||||
        additionalParametersBuilder: FormBuilder.() -> Unit = {},
 | 
			
		||||
        dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
 | 
			
		||||
        requestBuilder: HttpRequestBuilder.() -> Unit = {},
 | 
			
		||||
    ): ResultType = client.unimultipart(url, filename, inputProvider, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat)
 | 
			
		||||
 | 
			
		||||
    suspend fun <BodyType, ResultType> unimultipart(
 | 
			
		||||
        url: String,
 | 
			
		||||
        filename: String,
 | 
			
		||||
        inputProvider: InputProvider,
 | 
			
		||||
        otherData: BodyPair<BodyType>,
 | 
			
		||||
        resultDeserializer: DeserializationStrategy<ResultType>,
 | 
			
		||||
        mimetype: String = "*/*",
 | 
			
		||||
        additionalParametersBuilder: FormBuilder.() -> Unit = {},
 | 
			
		||||
        dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
 | 
			
		||||
        requestBuilder: HttpRequestBuilder.() -> Unit = {},
 | 
			
		||||
    ): ResultType = client.unimultipart(url, filename, otherData, inputProvider, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat)
 | 
			
		||||
 | 
			
		||||
    suspend fun <ResultType> unimultipart(
 | 
			
		||||
        url: String,
 | 
			
		||||
        mppFile: MPPFile,
 | 
			
		||||
        resultDeserializer: DeserializationStrategy<ResultType>,
 | 
			
		||||
        mimetype: String = "*/*",
 | 
			
		||||
        additionalParametersBuilder: FormBuilder.() -> Unit = {},
 | 
			
		||||
        dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
 | 
			
		||||
        requestBuilder: HttpRequestBuilder.() -> Unit = {}
 | 
			
		||||
    ): ResultType = client.unimultipart(
 | 
			
		||||
        url, mppFile, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    suspend fun <BodyType, ResultType> unimultipart(
 | 
			
		||||
        url: String,
 | 
			
		||||
        mppFile: MPPFile,
 | 
			
		||||
        otherData: BodyPair<BodyType>,
 | 
			
		||||
        resultDeserializer: DeserializationStrategy<ResultType>,
 | 
			
		||||
        mimetype: String = "*/*",
 | 
			
		||||
        additionalParametersBuilder: FormBuilder.() -> Unit = {},
 | 
			
		||||
        dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
 | 
			
		||||
        requestBuilder: HttpRequestBuilder.() -> Unit = {}
 | 
			
		||||
    ): ResultType = client.unimultipart(
 | 
			
		||||
        url, mppFile, otherData, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    fun <T> createStandardWebsocketFlow(
 | 
			
		||||
        url: String,
 | 
			
		||||
        checkReconnection: (Throwable?) -> Boolean = { true },
 | 
			
		||||
@@ -69,3 +121,124 @@ suspend fun <BodyType, ResultType> HttpClient.unipost(
 | 
			
		||||
}.let {
 | 
			
		||||
    serialFormat.decodeDefault(resultDeserializer, it)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun <ResultType> HttpClient.unimultipart(
 | 
			
		||||
    url: String,
 | 
			
		||||
    filename: String,
 | 
			
		||||
    inputProvider: InputProvider,
 | 
			
		||||
    resultDeserializer: DeserializationStrategy<ResultType>,
 | 
			
		||||
    mimetype: String = "*/*",
 | 
			
		||||
    additionalParametersBuilder: FormBuilder.() -> Unit = {},
 | 
			
		||||
    dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
 | 
			
		||||
    requestBuilder: HttpRequestBuilder.() -> Unit = {},
 | 
			
		||||
    serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
 | 
			
		||||
): ResultType = submitFormWithBinaryData<StandardKtorSerialInputData>(
 | 
			
		||||
    url,
 | 
			
		||||
    formData = formData {
 | 
			
		||||
        append(
 | 
			
		||||
            "bytes",
 | 
			
		||||
            inputProvider,
 | 
			
		||||
            Headers.build {
 | 
			
		||||
                append(HttpHeaders.ContentType, mimetype)
 | 
			
		||||
                append(HttpHeaders.ContentDisposition, "filename=\"$filename\"")
 | 
			
		||||
                dataHeadersBuilder()
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        additionalParametersBuilder()
 | 
			
		||||
    }
 | 
			
		||||
) {
 | 
			
		||||
    requestBuilder()
 | 
			
		||||
}.let { serialFormat.decodeDefault(resultDeserializer, it) }
 | 
			
		||||
 | 
			
		||||
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
 | 
			
		||||
    url: String,
 | 
			
		||||
    filename: String,
 | 
			
		||||
    otherData: BodyPair<BodyType>,
 | 
			
		||||
    inputProvider: InputProvider,
 | 
			
		||||
    resultDeserializer: DeserializationStrategy<ResultType>,
 | 
			
		||||
    mimetype: String = "*/*",
 | 
			
		||||
    additionalParametersBuilder: FormBuilder.() -> Unit = {},
 | 
			
		||||
    dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
 | 
			
		||||
    requestBuilder: HttpRequestBuilder.() -> Unit = {},
 | 
			
		||||
    serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
 | 
			
		||||
): ResultType = unimultipart(
 | 
			
		||||
    url,
 | 
			
		||||
    filename,
 | 
			
		||||
    inputProvider,
 | 
			
		||||
    resultDeserializer,
 | 
			
		||||
    mimetype,
 | 
			
		||||
    additionalParametersBuilder = {
 | 
			
		||||
        val serialized = serialFormat.encodeDefault(otherData.first, otherData.second)
 | 
			
		||||
        append(
 | 
			
		||||
            "data",
 | 
			
		||||
            InputProvider(serialized.size.toLong()) {
 | 
			
		||||
                ByteReadPacket(serialized)
 | 
			
		||||
            },
 | 
			
		||||
            Headers.build {
 | 
			
		||||
                append(HttpHeaders.ContentType, ContentType.Application.Cbor.contentType)
 | 
			
		||||
                append(HttpHeaders.ContentDisposition, "filename=data.bytes")
 | 
			
		||||
                dataHeadersBuilder()
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        additionalParametersBuilder()
 | 
			
		||||
    },
 | 
			
		||||
    dataHeadersBuilder,
 | 
			
		||||
    requestBuilder,
 | 
			
		||||
    serialFormat
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
suspend fun <ResultType> HttpClient.unimultipart(
 | 
			
		||||
    url: String,
 | 
			
		||||
    mppFile: MPPFile,
 | 
			
		||||
    resultDeserializer: DeserializationStrategy<ResultType>,
 | 
			
		||||
    mimetype: String = "*/*",
 | 
			
		||||
    additionalParametersBuilder: FormBuilder.() -> Unit = {},
 | 
			
		||||
    dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
 | 
			
		||||
    requestBuilder: HttpRequestBuilder.() -> Unit = {},
 | 
			
		||||
    serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
 | 
			
		||||
): ResultType = unimultipart(
 | 
			
		||||
    url,
 | 
			
		||||
    mppFile.filename.string,
 | 
			
		||||
    mppFile.inputProvider(),
 | 
			
		||||
    resultDeserializer,
 | 
			
		||||
    mimetype,
 | 
			
		||||
    additionalParametersBuilder,
 | 
			
		||||
    dataHeadersBuilder,
 | 
			
		||||
    requestBuilder,
 | 
			
		||||
    serialFormat
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
 | 
			
		||||
    url: String,
 | 
			
		||||
    mppFile: MPPFile,
 | 
			
		||||
    otherData: BodyPair<BodyType>,
 | 
			
		||||
    resultDeserializer: DeserializationStrategy<ResultType>,
 | 
			
		||||
    mimetype: String = "*/*",
 | 
			
		||||
    additionalParametersBuilder: FormBuilder.() -> Unit = {},
 | 
			
		||||
    dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
 | 
			
		||||
    requestBuilder: HttpRequestBuilder.() -> Unit = {},
 | 
			
		||||
    serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
 | 
			
		||||
): ResultType = unimultipart(
 | 
			
		||||
    url,
 | 
			
		||||
    mppFile,
 | 
			
		||||
    resultDeserializer,
 | 
			
		||||
    mimetype,
 | 
			
		||||
    additionalParametersBuilder = {
 | 
			
		||||
        val serialized = serialFormat.encodeDefault(otherData.first, otherData.second)
 | 
			
		||||
        append(
 | 
			
		||||
            "data",
 | 
			
		||||
            InputProvider(serialized.size.toLong()) {
 | 
			
		||||
                ByteReadPacket(serialized)
 | 
			
		||||
            },
 | 
			
		||||
            Headers.build {
 | 
			
		||||
                append(HttpHeaders.ContentType, ContentType.Application.Cbor.contentType)
 | 
			
		||||
                append(HttpHeaders.ContentDisposition, "filename=data.bytes")
 | 
			
		||||
                dataHeadersBuilder()
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        additionalParametersBuilder()
 | 
			
		||||
    },
 | 
			
		||||
    dataHeadersBuilder,
 | 
			
		||||
    requestBuilder,
 | 
			
		||||
    serialFormat
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
package dev.inmo.micro_utils.ktor.client
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.common.*
 | 
			
		||||
import io.ktor.client.request.forms.InputProvider
 | 
			
		||||
import io.ktor.utils.io.core.ByteReadPacket
 | 
			
		||||
 | 
			
		||||
actual suspend fun MPPFile.inputProvider(): InputProvider = bytes().let {
 | 
			
		||||
    InputProvider(it.size.toLong()) {
 | 
			
		||||
        ByteReadPacket(it)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
package dev.inmo.micro_utils.ktor.client
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.common.MPPFile
 | 
			
		||||
import io.ktor.client.request.forms.InputProvider
 | 
			
		||||
import io.ktor.utils.io.streams.asInput
 | 
			
		||||
 | 
			
		||||
actual suspend fun MPPFile.inputProvider(): InputProvider = InputProvider(length()) {
 | 
			
		||||
    inputStream().asInput()
 | 
			
		||||
}
 | 
			
		||||
@@ -10,6 +10,7 @@ kotlin {
 | 
			
		||||
    sourceSets {
 | 
			
		||||
        commonMain {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                api internalProject("micro_utils.common")
 | 
			
		||||
                api "org.jetbrains.kotlinx:kotlinx-serialization-cbor:$kotlin_serialisation_core_version"
 | 
			
		||||
                api "com.soywiz.korlibs.klock:klock:$klockVersion"
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,31 @@
 | 
			
		||||
package dev.inmo.micro_utils.ktor.server
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.common.*
 | 
			
		||||
import dev.inmo.micro_utils.coroutines.safely
 | 
			
		||||
import dev.inmo.micro_utils.ktor.common.*
 | 
			
		||||
import io.ktor.application.ApplicationCall
 | 
			
		||||
import io.ktor.application.call
 | 
			
		||||
import io.ktor.http.ContentType
 | 
			
		||||
import io.ktor.http.HttpStatusCode
 | 
			
		||||
import io.ktor.http.content.PartData
 | 
			
		||||
import io.ktor.http.content.forEachPart
 | 
			
		||||
import io.ktor.request.receive
 | 
			
		||||
import io.ktor.request.receiveMultipart
 | 
			
		||||
import io.ktor.response.respond
 | 
			
		||||
import io.ktor.response.respondBytes
 | 
			
		||||
import io.ktor.routing.Route
 | 
			
		||||
import io.ktor.util.asStream
 | 
			
		||||
import io.ktor.util.cio.writeChannel
 | 
			
		||||
import io.ktor.util.pipeline.PipelineContext
 | 
			
		||||
import io.ktor.utils.io.core.*
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.serialization.*
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.File.createTempFile
 | 
			
		||||
 | 
			
		||||
class UnifiedRouter(
 | 
			
		||||
    private val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
 | 
			
		||||
    private val serialFormatContentType: ContentType = standardKtorSerialFormatContentType
 | 
			
		||||
    val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
 | 
			
		||||
    val serialFormatContentType: ContentType = standardKtorSerialFormatContentType
 | 
			
		||||
) {
 | 
			
		||||
    fun <T> Route.includeWebsocketHandling(
 | 
			
		||||
        suburl: String,
 | 
			
		||||
@@ -104,6 +113,139 @@ suspend fun <T> ApplicationCall.uniload(
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun ApplicationCall.uniloadMultipart(
 | 
			
		||||
    onFormItem: (PartData.FormItem) -> Unit = {},
 | 
			
		||||
    onCustomFileItem: (PartData.FileItem) -> Unit = {},
 | 
			
		||||
    onBinaryContent: (PartData.BinaryItem) -> Unit = {}
 | 
			
		||||
) = safely {
 | 
			
		||||
    val multipartData = receiveMultipart()
 | 
			
		||||
 | 
			
		||||
    var resultInput: Input? = null
 | 
			
		||||
 | 
			
		||||
    multipartData.forEachPart {
 | 
			
		||||
        when (it) {
 | 
			
		||||
            is PartData.FormItem -> onFormItem(it)
 | 
			
		||||
            is PartData.FileItem -> {
 | 
			
		||||
                when (it.name) {
 | 
			
		||||
                    "bytes" -> resultInput = it.provider()
 | 
			
		||||
                    else -> onCustomFileItem(it)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            is PartData.BinaryItem -> onBinaryContent(it)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resultInput ?: error("Bytes has not been received")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun <T> ApplicationCall.uniloadMultipart(
 | 
			
		||||
    deserializer: DeserializationStrategy<T>,
 | 
			
		||||
    onFormItem: (PartData.FormItem) -> Unit = {},
 | 
			
		||||
    onCustomFileItem: (PartData.FileItem) -> Unit = {},
 | 
			
		||||
    onBinaryContent: (PartData.BinaryItem) -> Unit = {}
 | 
			
		||||
): Pair<Input, T> {
 | 
			
		||||
    var data: Optional<T>? = null
 | 
			
		||||
    val resultInput = uniloadMultipart(
 | 
			
		||||
        onFormItem,
 | 
			
		||||
        {
 | 
			
		||||
            if (it.name == "data") {
 | 
			
		||||
                data = standardKtorSerialFormat.decodeDefault(deserializer, it.provider().readBytes()).optional
 | 
			
		||||
            } else {
 | 
			
		||||
                onCustomFileItem(it)
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        onBinaryContent
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    val completeData = data ?: error("Data has not been received")
 | 
			
		||||
    return resultInput to (completeData.dataOrNull().let { it as T })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun <T> ApplicationCall.uniloadMultipartFile(
 | 
			
		||||
    deserializer: DeserializationStrategy<T>,
 | 
			
		||||
    onFormItem: (PartData.FormItem) -> Unit = {},
 | 
			
		||||
    onCustomFileItem: (PartData.FileItem) -> Unit = {},
 | 
			
		||||
    onBinaryContent: (PartData.BinaryItem) -> Unit = {},
 | 
			
		||||
) = safely {
 | 
			
		||||
    val multipartData = receiveMultipart()
 | 
			
		||||
 | 
			
		||||
    var resultInput: MPPFile? = null
 | 
			
		||||
    var data: Optional<T>? = null
 | 
			
		||||
 | 
			
		||||
    multipartData.forEachPart {
 | 
			
		||||
        when (it) {
 | 
			
		||||
            is PartData.FormItem -> onFormItem(it)
 | 
			
		||||
            is PartData.FileItem -> {
 | 
			
		||||
                when (it.name) {
 | 
			
		||||
                    "bytes" -> {
 | 
			
		||||
                        val name = FileName(it.originalFileName ?: error("File name is unknown for default part"))
 | 
			
		||||
                        resultInput = MPPFile.createTempFile(
 | 
			
		||||
                            name.nameWithoutExtension.let {
 | 
			
		||||
                                var resultName = it
 | 
			
		||||
                                while (resultName.length < 3) {
 | 
			
		||||
                                    resultName += "_"
 | 
			
		||||
                                }
 | 
			
		||||
                                resultName
 | 
			
		||||
                            },
 | 
			
		||||
                            ".${name.extension}"
 | 
			
		||||
                        ).apply {
 | 
			
		||||
                            outputStream().use { fileStream ->
 | 
			
		||||
                                it.provider().asStream().copyTo(fileStream)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    "data" -> data = standardKtorSerialFormat.decodeDefault(deserializer, it.provider().readBytes()).optional
 | 
			
		||||
                    else -> onCustomFileItem(it)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            is PartData.BinaryItem -> onBinaryContent(it)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val completeData = data ?: error("Data has not been received")
 | 
			
		||||
    (resultInput ?: error("Bytes has not been received")) to (completeData.dataOrNull().let { it as T })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun ApplicationCall.uniloadMultipartFile(
 | 
			
		||||
    onFormItem: (PartData.FormItem) -> Unit = {},
 | 
			
		||||
    onCustomFileItem: (PartData.FileItem) -> Unit = {},
 | 
			
		||||
    onBinaryContent: (PartData.BinaryItem) -> Unit = {},
 | 
			
		||||
) = safely {
 | 
			
		||||
    val multipartData = receiveMultipart()
 | 
			
		||||
 | 
			
		||||
    var resultInput: MPPFile? = null
 | 
			
		||||
 | 
			
		||||
    multipartData.forEachPart {
 | 
			
		||||
        when (it) {
 | 
			
		||||
            is PartData.FormItem -> onFormItem(it)
 | 
			
		||||
            is PartData.FileItem -> {
 | 
			
		||||
                if (it.name == "bytes") {
 | 
			
		||||
                    val name = FileName(it.originalFileName ?: error("File name is unknown for default part"))
 | 
			
		||||
                    resultInput = MPPFile.createTempFile(
 | 
			
		||||
                        name.nameWithoutExtension.let {
 | 
			
		||||
                            var resultName = it
 | 
			
		||||
                            while (resultName.length < 3) {
 | 
			
		||||
                                resultName += "_"
 | 
			
		||||
                            }
 | 
			
		||||
                            resultName
 | 
			
		||||
                        },
 | 
			
		||||
                        ".${name.extension}"
 | 
			
		||||
                    ).apply {
 | 
			
		||||
                        outputStream().use { fileStream ->
 | 
			
		||||
                            it.provider().asStream().copyTo(fileStream)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    onCustomFileItem(it)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            is PartData.BinaryItem -> onBinaryContent(it)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resultInput ?: error("Bytes has not been received")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun ApplicationCall.getParameterOrSendError(
 | 
			
		||||
    field: String
 | 
			
		||||
) = parameters[field].also {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package dev.inmo.micro_utils.ktor.server
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator
 | 
			
		||||
import io.ktor.application.Application
 | 
			
		||||
import io.ktor.server.cio.CIO
 | 
			
		||||
import io.ktor.server.engine.*
 | 
			
		||||
@@ -31,3 +32,27 @@ fun createKtorServer(
 | 
			
		||||
    port: Int = Random.nextInt(1024, 65535),
 | 
			
		||||
    block: Application.() -> Unit
 | 
			
		||||
): ApplicationEngine = createKtorServer(CIO, host, port, block)
 | 
			
		||||
 | 
			
		||||
fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> createKtorServer(
 | 
			
		||||
    engine: ApplicationEngineFactory<TEngine, TConfiguration>,
 | 
			
		||||
    host: String = "localhost",
 | 
			
		||||
    port: Int = Random.nextInt(1024, 65535),
 | 
			
		||||
    configurators: List<KtorApplicationConfigurator>
 | 
			
		||||
): TEngine = createKtorServer(
 | 
			
		||||
    engine,
 | 
			
		||||
    host,
 | 
			
		||||
    port
 | 
			
		||||
) {
 | 
			
		||||
    configurators.forEach { it.apply { configure() } }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create server with [CIO] server engine without starting of it
 | 
			
		||||
 *
 | 
			
		||||
 * @see ApplicationEngine.start
 | 
			
		||||
 */
 | 
			
		||||
fun createKtorServer(
 | 
			
		||||
    host: String = "localhost",
 | 
			
		||||
    port: Int = Random.nextInt(1024, 65535),
 | 
			
		||||
    configurators: List<KtorApplicationConfigurator>
 | 
			
		||||
): ApplicationEngine = createKtorServer(CIO, host, port, configurators)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								language_codes/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								language_codes/build.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
plugins {
 | 
			
		||||
    id "org.jetbrains.kotlin.multiplatform"
 | 
			
		||||
    id "org.jetbrains.kotlin.plugin.serialization"
 | 
			
		||||
    id "com.android.library"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$mppProjectWithSerializationPresetPath"
 | 
			
		||||
							
								
								
									
										31
									
								
								language_codes/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								language_codes/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
buildscript {
 | 
			
		||||
    repositories {
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dependencies {
 | 
			
		||||
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 | 
			
		||||
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    id 'org.jetbrains.kotlin.jvm'
 | 
			
		||||
    id "org.jetbrains.kotlin.plugin.serialization"
 | 
			
		||||
    id "application"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
 | 
			
		||||
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_core_version"
 | 
			
		||||
 | 
			
		||||
    implementation "io.ktor:ktor-client-core:$ktor_version"
 | 
			
		||||
    implementation "io.ktor:ktor-client-java:$ktor_version"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mainClassName="MainKt"
 | 
			
		||||
 | 
			
		||||
java {
 | 
			
		||||
    sourceCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
    targetCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										214
									
								
								language_codes/generator/src/main/kotlin/Main.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								language_codes/generator/src/main/kotlin/Main.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,214 @@
 | 
			
		||||
import io.ktor.client.HttpClient
 | 
			
		||||
import io.ktor.client.request.get
 | 
			
		||||
import kotlinx.serialization.SerialName
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
import kotlinx.serialization.builtins.ListSerializer
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.text.Normalizer
 | 
			
		||||
 | 
			
		||||
private val json = Json {
 | 
			
		||||
    ignoreUnknownKeys = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private const val baseClassName = "IetfLanguageCode"
 | 
			
		||||
private const val unknownBaseClassName = "Unknown$baseClassName"
 | 
			
		||||
private const val baseClassSerializerName = "IetfLanguageCodeSerializer"
 | 
			
		||||
private const val baseClassSerializerAnnotationName = "@Serializable(${baseClassSerializerName}::class)"
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
private data class LanguageCode(
 | 
			
		||||
    @SerialName("alpha2")
 | 
			
		||||
    val tag: String,
 | 
			
		||||
    @SerialName("English")
 | 
			
		||||
    val title: String
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
fun String.adaptAsTitle() = if (first().isDigit()) {
 | 
			
		||||
    "L$this"
 | 
			
		||||
} else {
 | 
			
		||||
    this
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun String.normalized() = Normalizer.normalize(this, Normalizer.Form.NFD).replace(Regex("[^\\p{ASCII}]"), "")
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
private data class LanguageCodeWithTag(
 | 
			
		||||
    @SerialName("langType")
 | 
			
		||||
    val tag: String,
 | 
			
		||||
    @SerialName("lang")
 | 
			
		||||
    val withSubtag: String
 | 
			
		||||
) {
 | 
			
		||||
    val partWithoutTag: String
 | 
			
		||||
        get() {
 | 
			
		||||
            return withSubtag.substring(
 | 
			
		||||
                withSubtag.indexOf("-") + 1, withSubtag.length
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    val middleTag
 | 
			
		||||
        get() = if (partWithoutTag.contains("-")) {
 | 
			
		||||
            partWithoutTag.substring(0, partWithoutTag.indexOf("-"))
 | 
			
		||||
        } else {
 | 
			
		||||
            null
 | 
			
		||||
        }
 | 
			
		||||
    val middleTagTitle
 | 
			
		||||
        get() = middleTag ?.adaptAsTitle() ?: partWithoutTag.adaptAsTitle()
 | 
			
		||||
    val subtag: String
 | 
			
		||||
        get() = middleTag ?: partWithoutTag
 | 
			
		||||
    val endTag
 | 
			
		||||
        get() = if (partWithoutTag.contains("-")) {
 | 
			
		||||
            partWithoutTag.substring(partWithoutTag.indexOf("-") + 1, partWithoutTag.length)
 | 
			
		||||
        } else {
 | 
			
		||||
            null
 | 
			
		||||
        }
 | 
			
		||||
    val endTagAsTitle
 | 
			
		||||
        get() = endTag ?.adaptAsTitle()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
data class Tag(
 | 
			
		||||
    val title: String,
 | 
			
		||||
    val tag: String,
 | 
			
		||||
    val subtags: List<Tag>
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
private fun printLanguageCodeAndTags(
 | 
			
		||||
    tag: Tag,
 | 
			
		||||
    parent: Tag? = null,
 | 
			
		||||
    indents: String = "    "
 | 
			
		||||
): String = if (tag.subtags.isEmpty()) {
 | 
			
		||||
"""${indents}${baseClassSerializerAnnotationName}
 | 
			
		||||
${indents}object ${tag.title} : ${parent ?.title ?: baseClassName}() { override val code: String = "${tag.tag}" }"""
 | 
			
		||||
} else {
 | 
			
		||||
"""
 | 
			
		||||
${indents}${baseClassSerializerAnnotationName}
 | 
			
		||||
${indents}sealed class ${tag.title} : ${parent ?.title ?: baseClassName}() {
 | 
			
		||||
${indents}    override val code: String = "${tag.tag}"
 | 
			
		||||
 | 
			
		||||
${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents}    ") }}
 | 
			
		||||
 | 
			
		||||
${indents}    ${baseClassSerializerAnnotationName}
 | 
			
		||||
${indents}    companion object : ${tag.title}()
 | 
			
		||||
${indents}}
 | 
			
		||||
"""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun buildKtFileContent(tags: List<Tag>): String = """
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class has been automatically generated using
 | 
			
		||||
 * https://github.com/InsanusMokrassar/MicroUtils/tree/master/language_codes/generator . This generator uses
 | 
			
		||||
 * https://datahub.io/core/language-codes/ files (base and tags) and create the whole hierarchy using it.
 | 
			
		||||
 */
 | 
			
		||||
${baseClassSerializerAnnotationName}
 | 
			
		||||
sealed class $baseClassName {
 | 
			
		||||
    abstract val code: String
 | 
			
		||||
 | 
			
		||||
${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = "    ") } }
 | 
			
		||||
 | 
			
		||||
    $baseClassSerializerAnnotationName
 | 
			
		||||
    data class $unknownBaseClassName (override val code: String) : $baseClassName()
 | 
			
		||||
 | 
			
		||||
    override fun toString() = code
 | 
			
		||||
}
 | 
			
		||||
""".trimIndent()
 | 
			
		||||
 | 
			
		||||
fun createStringConverterCode(tags: List<Tag>): String {
 | 
			
		||||
    fun createDeserializeVariantForTag(
 | 
			
		||||
        tag: Tag,
 | 
			
		||||
        pretitle: String = baseClassName,
 | 
			
		||||
        indents: String = "        "
 | 
			
		||||
    ): String {
 | 
			
		||||
        val currentTitle = "$pretitle.${tag.title}"
 | 
			
		||||
        return """${indents}$currentTitle.code -> $currentTitle${if (tag.subtags.isNotEmpty()) tag.subtags.joinToString("\n", "\n") { createDeserializeVariantForTag(it, currentTitle, indents) } else ""}"""
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return """fun String.as$baseClassName(): $baseClassName {
 | 
			
		||||
    return when (this) {
 | 
			
		||||
${tags.joinToString("\n") { createDeserializeVariantForTag(it) }}
 | 
			
		||||
        else -> $baseClassName.${unknownBaseClassName}(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
fun convertTo$baseClassName(code: String) = code.as$baseClassName()
 | 
			
		||||
fun $baseClassName(code: String) = code.as$baseClassName()
 | 
			
		||||
"""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun createSerializerCode(tags: List<Tag>): String {
 | 
			
		||||
    return """import kotlinx.serialization.KSerializer
 | 
			
		||||
import kotlinx.serialization.builtins.serializer
 | 
			
		||||
import kotlinx.serialization.encoding.Decoder
 | 
			
		||||
import kotlinx.serialization.encoding.Encoder
 | 
			
		||||
 | 
			
		||||
object $baseClassSerializerName : KSerializer<$baseClassName> {
 | 
			
		||||
    override val descriptor = String.serializer().descriptor
 | 
			
		||||
 | 
			
		||||
    override fun deserialize(decoder: Decoder): $baseClassName {
 | 
			
		||||
        return $baseClassName(decoder.decodeString())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun serialize(encoder: Encoder, value: IetfLanguageCode) {
 | 
			
		||||
        encoder.encodeString(value.code)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
"""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun main(vararg args: String) {
 | 
			
		||||
    val outputFolder = args.firstOrNull() ?.let { File(it) }
 | 
			
		||||
    outputFolder ?.mkdirs()
 | 
			
		||||
    val ietfLanguageCodesLink = "https://datahub.io/core/language-codes/r/language-codes.json"
 | 
			
		||||
    val ietfLanguageCodesAdditionalTagsLink = "https://datahub.io/core/language-codes/r/ietf-language-tags.json"
 | 
			
		||||
 | 
			
		||||
    val client = HttpClient()
 | 
			
		||||
 | 
			
		||||
    val ietfLanguageCodes = json.decodeFromString(
 | 
			
		||||
        ListSerializer(LanguageCode.serializer()),
 | 
			
		||||
        client.get(ietfLanguageCodesLink)
 | 
			
		||||
    ).map {
 | 
			
		||||
        it.copy(
 | 
			
		||||
            title = it.title
 | 
			
		||||
                .replace(Regex("[;,()-]"), "")
 | 
			
		||||
                .split(" ")
 | 
			
		||||
                .joinToString("") { "${it.first().uppercase()}${it.substring(1)}" }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    val ietfLanguageCodesWithTagsMap = json.decodeFromString(
 | 
			
		||||
        ListSerializer(LanguageCodeWithTag.serializer()),
 | 
			
		||||
        client.get(ietfLanguageCodesAdditionalTagsLink)
 | 
			
		||||
    ).filter { it.withSubtag != it.tag }.groupBy { it.tag }
 | 
			
		||||
 | 
			
		||||
    val tags = ietfLanguageCodes.map {
 | 
			
		||||
        val unformattedSubtags = ietfLanguageCodesWithTagsMap[it.tag] ?: emptyList()
 | 
			
		||||
        val threeLevelTags = unformattedSubtags.filter { it.endTag != null }.groupBy { it.middleTag }
 | 
			
		||||
        val subtags = unformattedSubtags.mapNotNull {
 | 
			
		||||
            if (it.endTag == null) {
 | 
			
		||||
                val currentSubtags = (threeLevelTags[it.subtag] ?: emptyList()).map {
 | 
			
		||||
                    Tag(it.endTagAsTitle!!.normalized(), it.withSubtag, emptyList())
 | 
			
		||||
                }
 | 
			
		||||
                Tag(it.middleTagTitle.normalized(), it.withSubtag, currentSubtags)
 | 
			
		||||
            } else {
 | 
			
		||||
                null
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Tag(it.title.normalized(), it.tag, subtags)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    File(outputFolder, "LanguageCodes.kt").apply {
 | 
			
		||||
        delete()
 | 
			
		||||
        createNewFile()
 | 
			
		||||
        writeText(buildKtFileContent(tags))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    File(outputFolder, "StringToLanguageCodes.kt").apply {
 | 
			
		||||
        delete()
 | 
			
		||||
        createNewFile()
 | 
			
		||||
        writeText(createStringConverterCode(tags))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    File(outputFolder, "$baseClassSerializerName.kt").apply {
 | 
			
		||||
        delete()
 | 
			
		||||
        createNewFile()
 | 
			
		||||
        writeText(createSerializerCode(tags))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
package dev.inmo.micro_utils.language_codes
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.KSerializer
 | 
			
		||||
import kotlinx.serialization.builtins.serializer
 | 
			
		||||
import kotlinx.serialization.encoding.Decoder
 | 
			
		||||
import kotlinx.serialization.encoding.Encoder
 | 
			
		||||
 | 
			
		||||
object IetfLanguageCodeSerializer : KSerializer<IetfLanguageCode> {
 | 
			
		||||
    override val descriptor = String.serializer().descriptor
 | 
			
		||||
 | 
			
		||||
    override fun deserialize(decoder: Decoder): IetfLanguageCode {
 | 
			
		||||
        return IetfLanguageCode(decoder.decodeString())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun serialize(encoder: Encoder, value: IetfLanguageCode) {
 | 
			
		||||
        encoder.encodeString(value.code)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,671 @@
 | 
			
		||||
package dev.inmo.micro_utils.language_codes
 | 
			
		||||
 | 
			
		||||
fun String.asIetfLanguageCode(): IetfLanguageCode {
 | 
			
		||||
    return when (this) {
 | 
			
		||||
        IetfLanguageCode.Afar.code -> IetfLanguageCode.Afar
 | 
			
		||||
        IetfLanguageCode.Abkhazian.code -> IetfLanguageCode.Abkhazian
 | 
			
		||||
        IetfLanguageCode.Avestan.code -> IetfLanguageCode.Avestan
 | 
			
		||||
        IetfLanguageCode.Afrikaans.code -> IetfLanguageCode.Afrikaans
 | 
			
		||||
        IetfLanguageCode.Afrikaans.NA.code -> IetfLanguageCode.Afrikaans.NA
 | 
			
		||||
        IetfLanguageCode.Afrikaans.ZA.code -> IetfLanguageCode.Afrikaans.ZA
 | 
			
		||||
        IetfLanguageCode.Akan.code -> IetfLanguageCode.Akan
 | 
			
		||||
        IetfLanguageCode.Akan.GH.code -> IetfLanguageCode.Akan.GH
 | 
			
		||||
        IetfLanguageCode.Amharic.code -> IetfLanguageCode.Amharic
 | 
			
		||||
        IetfLanguageCode.Amharic.ET.code -> IetfLanguageCode.Amharic.ET
 | 
			
		||||
        IetfLanguageCode.Aragonese.code -> IetfLanguageCode.Aragonese
 | 
			
		||||
        IetfLanguageCode.Arabic.code -> IetfLanguageCode.Arabic
 | 
			
		||||
        IetfLanguageCode.Arabic.L001.code -> IetfLanguageCode.Arabic.L001
 | 
			
		||||
        IetfLanguageCode.Arabic.AE.code -> IetfLanguageCode.Arabic.AE
 | 
			
		||||
        IetfLanguageCode.Arabic.BH.code -> IetfLanguageCode.Arabic.BH
 | 
			
		||||
        IetfLanguageCode.Arabic.DJ.code -> IetfLanguageCode.Arabic.DJ
 | 
			
		||||
        IetfLanguageCode.Arabic.DZ.code -> IetfLanguageCode.Arabic.DZ
 | 
			
		||||
        IetfLanguageCode.Arabic.EG.code -> IetfLanguageCode.Arabic.EG
 | 
			
		||||
        IetfLanguageCode.Arabic.EH.code -> IetfLanguageCode.Arabic.EH
 | 
			
		||||
        IetfLanguageCode.Arabic.ER.code -> IetfLanguageCode.Arabic.ER
 | 
			
		||||
        IetfLanguageCode.Arabic.IL.code -> IetfLanguageCode.Arabic.IL
 | 
			
		||||
        IetfLanguageCode.Arabic.IQ.code -> IetfLanguageCode.Arabic.IQ
 | 
			
		||||
        IetfLanguageCode.Arabic.JO.code -> IetfLanguageCode.Arabic.JO
 | 
			
		||||
        IetfLanguageCode.Arabic.KM.code -> IetfLanguageCode.Arabic.KM
 | 
			
		||||
        IetfLanguageCode.Arabic.KW.code -> IetfLanguageCode.Arabic.KW
 | 
			
		||||
        IetfLanguageCode.Arabic.LB.code -> IetfLanguageCode.Arabic.LB
 | 
			
		||||
        IetfLanguageCode.Arabic.LY.code -> IetfLanguageCode.Arabic.LY
 | 
			
		||||
        IetfLanguageCode.Arabic.MA.code -> IetfLanguageCode.Arabic.MA
 | 
			
		||||
        IetfLanguageCode.Arabic.MR.code -> IetfLanguageCode.Arabic.MR
 | 
			
		||||
        IetfLanguageCode.Arabic.OM.code -> IetfLanguageCode.Arabic.OM
 | 
			
		||||
        IetfLanguageCode.Arabic.PS.code -> IetfLanguageCode.Arabic.PS
 | 
			
		||||
        IetfLanguageCode.Arabic.QA.code -> IetfLanguageCode.Arabic.QA
 | 
			
		||||
        IetfLanguageCode.Arabic.SA.code -> IetfLanguageCode.Arabic.SA
 | 
			
		||||
        IetfLanguageCode.Arabic.SD.code -> IetfLanguageCode.Arabic.SD
 | 
			
		||||
        IetfLanguageCode.Arabic.SO.code -> IetfLanguageCode.Arabic.SO
 | 
			
		||||
        IetfLanguageCode.Arabic.SS.code -> IetfLanguageCode.Arabic.SS
 | 
			
		||||
        IetfLanguageCode.Arabic.SY.code -> IetfLanguageCode.Arabic.SY
 | 
			
		||||
        IetfLanguageCode.Arabic.TD.code -> IetfLanguageCode.Arabic.TD
 | 
			
		||||
        IetfLanguageCode.Arabic.TN.code -> IetfLanguageCode.Arabic.TN
 | 
			
		||||
        IetfLanguageCode.Arabic.YE.code -> IetfLanguageCode.Arabic.YE
 | 
			
		||||
        IetfLanguageCode.Assamese.code -> IetfLanguageCode.Assamese
 | 
			
		||||
        IetfLanguageCode.Assamese.IN.code -> IetfLanguageCode.Assamese.IN
 | 
			
		||||
        IetfLanguageCode.Avaric.code -> IetfLanguageCode.Avaric
 | 
			
		||||
        IetfLanguageCode.Aymara.code -> IetfLanguageCode.Aymara
 | 
			
		||||
        IetfLanguageCode.Azerbaijani.code -> IetfLanguageCode.Azerbaijani
 | 
			
		||||
        IetfLanguageCode.Azerbaijani.Cyrl.code -> IetfLanguageCode.Azerbaijani.Cyrl
 | 
			
		||||
        IetfLanguageCode.Azerbaijani.Cyrl.AZ.code -> IetfLanguageCode.Azerbaijani.Cyrl.AZ
 | 
			
		||||
        IetfLanguageCode.Azerbaijani.Latn.code -> IetfLanguageCode.Azerbaijani.Latn
 | 
			
		||||
        IetfLanguageCode.Azerbaijani.Latn.AZ.code -> IetfLanguageCode.Azerbaijani.Latn.AZ
 | 
			
		||||
        IetfLanguageCode.Bashkir.code -> IetfLanguageCode.Bashkir
 | 
			
		||||
        IetfLanguageCode.Belarusian.code -> IetfLanguageCode.Belarusian
 | 
			
		||||
        IetfLanguageCode.Belarusian.BY.code -> IetfLanguageCode.Belarusian.BY
 | 
			
		||||
        IetfLanguageCode.Bulgarian.code -> IetfLanguageCode.Bulgarian
 | 
			
		||||
        IetfLanguageCode.Bulgarian.BG.code -> IetfLanguageCode.Bulgarian.BG
 | 
			
		||||
        IetfLanguageCode.BihariLanguages.code -> IetfLanguageCode.BihariLanguages
 | 
			
		||||
        IetfLanguageCode.Bislama.code -> IetfLanguageCode.Bislama
 | 
			
		||||
        IetfLanguageCode.Bambara.code -> IetfLanguageCode.Bambara
 | 
			
		||||
        IetfLanguageCode.Bambara.ML.code -> IetfLanguageCode.Bambara.ML
 | 
			
		||||
        IetfLanguageCode.Bengali.code -> IetfLanguageCode.Bengali
 | 
			
		||||
        IetfLanguageCode.Bengali.BD.code -> IetfLanguageCode.Bengali.BD
 | 
			
		||||
        IetfLanguageCode.Bengali.IN.code -> IetfLanguageCode.Bengali.IN
 | 
			
		||||
        IetfLanguageCode.Tibetan.code -> IetfLanguageCode.Tibetan
 | 
			
		||||
        IetfLanguageCode.Tibetan.CN.code -> IetfLanguageCode.Tibetan.CN
 | 
			
		||||
        IetfLanguageCode.Tibetan.IN.code -> IetfLanguageCode.Tibetan.IN
 | 
			
		||||
        IetfLanguageCode.Breton.code -> IetfLanguageCode.Breton
 | 
			
		||||
        IetfLanguageCode.Breton.FR.code -> IetfLanguageCode.Breton.FR
 | 
			
		||||
        IetfLanguageCode.Bosnian.code -> IetfLanguageCode.Bosnian
 | 
			
		||||
        IetfLanguageCode.Bosnian.Cyrl.code -> IetfLanguageCode.Bosnian.Cyrl
 | 
			
		||||
        IetfLanguageCode.Bosnian.Cyrl.BA.code -> IetfLanguageCode.Bosnian.Cyrl.BA
 | 
			
		||||
        IetfLanguageCode.Bosnian.Latn.code -> IetfLanguageCode.Bosnian.Latn
 | 
			
		||||
        IetfLanguageCode.Bosnian.Latn.BA.code -> IetfLanguageCode.Bosnian.Latn.BA
 | 
			
		||||
        IetfLanguageCode.CatalanValencian.code -> IetfLanguageCode.CatalanValencian
 | 
			
		||||
        IetfLanguageCode.CatalanValencian.AD.code -> IetfLanguageCode.CatalanValencian.AD
 | 
			
		||||
        IetfLanguageCode.CatalanValencian.ES.code -> IetfLanguageCode.CatalanValencian.ES
 | 
			
		||||
        IetfLanguageCode.CatalanValencian.ES.VALENCIA.code -> IetfLanguageCode.CatalanValencian.ES.VALENCIA
 | 
			
		||||
        IetfLanguageCode.CatalanValencian.FR.code -> IetfLanguageCode.CatalanValencian.FR
 | 
			
		||||
        IetfLanguageCode.CatalanValencian.IT.code -> IetfLanguageCode.CatalanValencian.IT
 | 
			
		||||
        IetfLanguageCode.Chechen.code -> IetfLanguageCode.Chechen
 | 
			
		||||
        IetfLanguageCode.Chechen.RU.code -> IetfLanguageCode.Chechen.RU
 | 
			
		||||
        IetfLanguageCode.Chamorro.code -> IetfLanguageCode.Chamorro
 | 
			
		||||
        IetfLanguageCode.Corsican.code -> IetfLanguageCode.Corsican
 | 
			
		||||
        IetfLanguageCode.Cree.code -> IetfLanguageCode.Cree
 | 
			
		||||
        IetfLanguageCode.Czech.code -> IetfLanguageCode.Czech
 | 
			
		||||
        IetfLanguageCode.Czech.CZ.code -> IetfLanguageCode.Czech.CZ
 | 
			
		||||
        IetfLanguageCode.ChurchSlavicOldSlavonicChurchSlavonicOldBulgarianOldChurchSlavonic.code -> IetfLanguageCode.ChurchSlavicOldSlavonicChurchSlavonicOldBulgarianOldChurchSlavonic
 | 
			
		||||
        IetfLanguageCode.ChurchSlavicOldSlavonicChurchSlavonicOldBulgarianOldChurchSlavonic.RU.code -> IetfLanguageCode.ChurchSlavicOldSlavonicChurchSlavonicOldBulgarianOldChurchSlavonic.RU
 | 
			
		||||
        IetfLanguageCode.Chuvash.code -> IetfLanguageCode.Chuvash
 | 
			
		||||
        IetfLanguageCode.Welsh.code -> IetfLanguageCode.Welsh
 | 
			
		||||
        IetfLanguageCode.Welsh.GB.code -> IetfLanguageCode.Welsh.GB
 | 
			
		||||
        IetfLanguageCode.Danish.code -> IetfLanguageCode.Danish
 | 
			
		||||
        IetfLanguageCode.Danish.DK.code -> IetfLanguageCode.Danish.DK
 | 
			
		||||
        IetfLanguageCode.Danish.GL.code -> IetfLanguageCode.Danish.GL
 | 
			
		||||
        IetfLanguageCode.German.code -> IetfLanguageCode.German
 | 
			
		||||
        IetfLanguageCode.German.AT.code -> IetfLanguageCode.German.AT
 | 
			
		||||
        IetfLanguageCode.German.BE.code -> IetfLanguageCode.German.BE
 | 
			
		||||
        IetfLanguageCode.German.CH.code -> IetfLanguageCode.German.CH
 | 
			
		||||
        IetfLanguageCode.German.DE.code -> IetfLanguageCode.German.DE
 | 
			
		||||
        IetfLanguageCode.German.IT.code -> IetfLanguageCode.German.IT
 | 
			
		||||
        IetfLanguageCode.German.LI.code -> IetfLanguageCode.German.LI
 | 
			
		||||
        IetfLanguageCode.German.LU.code -> IetfLanguageCode.German.LU
 | 
			
		||||
        IetfLanguageCode.DivehiDhivehiMaldivian.code -> IetfLanguageCode.DivehiDhivehiMaldivian
 | 
			
		||||
        IetfLanguageCode.Dzongkha.code -> IetfLanguageCode.Dzongkha
 | 
			
		||||
        IetfLanguageCode.Dzongkha.BT.code -> IetfLanguageCode.Dzongkha.BT
 | 
			
		||||
        IetfLanguageCode.Ewe.code -> IetfLanguageCode.Ewe
 | 
			
		||||
        IetfLanguageCode.Ewe.GH.code -> IetfLanguageCode.Ewe.GH
 | 
			
		||||
        IetfLanguageCode.Ewe.TG.code -> IetfLanguageCode.Ewe.TG
 | 
			
		||||
        IetfLanguageCode.GreekModern1453.code -> IetfLanguageCode.GreekModern1453
 | 
			
		||||
        IetfLanguageCode.GreekModern1453.CY.code -> IetfLanguageCode.GreekModern1453.CY
 | 
			
		||||
        IetfLanguageCode.GreekModern1453.GR.code -> IetfLanguageCode.GreekModern1453.GR
 | 
			
		||||
        IetfLanguageCode.English.code -> IetfLanguageCode.English
 | 
			
		||||
        IetfLanguageCode.English.L001.code -> IetfLanguageCode.English.L001
 | 
			
		||||
        IetfLanguageCode.English.L150.code -> IetfLanguageCode.English.L150
 | 
			
		||||
        IetfLanguageCode.English.AE.code -> IetfLanguageCode.English.AE
 | 
			
		||||
        IetfLanguageCode.English.AG.code -> IetfLanguageCode.English.AG
 | 
			
		||||
        IetfLanguageCode.English.AI.code -> IetfLanguageCode.English.AI
 | 
			
		||||
        IetfLanguageCode.English.AS.code -> IetfLanguageCode.English.AS
 | 
			
		||||
        IetfLanguageCode.English.AT.code -> IetfLanguageCode.English.AT
 | 
			
		||||
        IetfLanguageCode.English.AU.code -> IetfLanguageCode.English.AU
 | 
			
		||||
        IetfLanguageCode.English.BB.code -> IetfLanguageCode.English.BB
 | 
			
		||||
        IetfLanguageCode.English.BE.code -> IetfLanguageCode.English.BE
 | 
			
		||||
        IetfLanguageCode.English.BI.code -> IetfLanguageCode.English.BI
 | 
			
		||||
        IetfLanguageCode.English.BM.code -> IetfLanguageCode.English.BM
 | 
			
		||||
        IetfLanguageCode.English.BS.code -> IetfLanguageCode.English.BS
 | 
			
		||||
        IetfLanguageCode.English.BW.code -> IetfLanguageCode.English.BW
 | 
			
		||||
        IetfLanguageCode.English.BZ.code -> IetfLanguageCode.English.BZ
 | 
			
		||||
        IetfLanguageCode.English.CA.code -> IetfLanguageCode.English.CA
 | 
			
		||||
        IetfLanguageCode.English.CC.code -> IetfLanguageCode.English.CC
 | 
			
		||||
        IetfLanguageCode.English.CH.code -> IetfLanguageCode.English.CH
 | 
			
		||||
        IetfLanguageCode.English.CK.code -> IetfLanguageCode.English.CK
 | 
			
		||||
        IetfLanguageCode.English.CM.code -> IetfLanguageCode.English.CM
 | 
			
		||||
        IetfLanguageCode.English.CX.code -> IetfLanguageCode.English.CX
 | 
			
		||||
        IetfLanguageCode.English.CY.code -> IetfLanguageCode.English.CY
 | 
			
		||||
        IetfLanguageCode.English.DE.code -> IetfLanguageCode.English.DE
 | 
			
		||||
        IetfLanguageCode.English.DG.code -> IetfLanguageCode.English.DG
 | 
			
		||||
        IetfLanguageCode.English.DK.code -> IetfLanguageCode.English.DK
 | 
			
		||||
        IetfLanguageCode.English.DM.code -> IetfLanguageCode.English.DM
 | 
			
		||||
        IetfLanguageCode.English.ER.code -> IetfLanguageCode.English.ER
 | 
			
		||||
        IetfLanguageCode.English.FI.code -> IetfLanguageCode.English.FI
 | 
			
		||||
        IetfLanguageCode.English.FJ.code -> IetfLanguageCode.English.FJ
 | 
			
		||||
        IetfLanguageCode.English.FK.code -> IetfLanguageCode.English.FK
 | 
			
		||||
        IetfLanguageCode.English.FM.code -> IetfLanguageCode.English.FM
 | 
			
		||||
        IetfLanguageCode.English.GB.code -> IetfLanguageCode.English.GB
 | 
			
		||||
        IetfLanguageCode.English.GD.code -> IetfLanguageCode.English.GD
 | 
			
		||||
        IetfLanguageCode.English.GG.code -> IetfLanguageCode.English.GG
 | 
			
		||||
        IetfLanguageCode.English.GH.code -> IetfLanguageCode.English.GH
 | 
			
		||||
        IetfLanguageCode.English.GI.code -> IetfLanguageCode.English.GI
 | 
			
		||||
        IetfLanguageCode.English.GM.code -> IetfLanguageCode.English.GM
 | 
			
		||||
        IetfLanguageCode.English.GU.code -> IetfLanguageCode.English.GU
 | 
			
		||||
        IetfLanguageCode.English.GY.code -> IetfLanguageCode.English.GY
 | 
			
		||||
        IetfLanguageCode.English.HK.code -> IetfLanguageCode.English.HK
 | 
			
		||||
        IetfLanguageCode.English.IE.code -> IetfLanguageCode.English.IE
 | 
			
		||||
        IetfLanguageCode.English.IL.code -> IetfLanguageCode.English.IL
 | 
			
		||||
        IetfLanguageCode.English.IM.code -> IetfLanguageCode.English.IM
 | 
			
		||||
        IetfLanguageCode.English.IN.code -> IetfLanguageCode.English.IN
 | 
			
		||||
        IetfLanguageCode.English.IO.code -> IetfLanguageCode.English.IO
 | 
			
		||||
        IetfLanguageCode.English.JE.code -> IetfLanguageCode.English.JE
 | 
			
		||||
        IetfLanguageCode.English.JM.code -> IetfLanguageCode.English.JM
 | 
			
		||||
        IetfLanguageCode.English.KE.code -> IetfLanguageCode.English.KE
 | 
			
		||||
        IetfLanguageCode.English.KI.code -> IetfLanguageCode.English.KI
 | 
			
		||||
        IetfLanguageCode.English.KN.code -> IetfLanguageCode.English.KN
 | 
			
		||||
        IetfLanguageCode.English.KY.code -> IetfLanguageCode.English.KY
 | 
			
		||||
        IetfLanguageCode.English.LC.code -> IetfLanguageCode.English.LC
 | 
			
		||||
        IetfLanguageCode.English.LR.code -> IetfLanguageCode.English.LR
 | 
			
		||||
        IetfLanguageCode.English.LS.code -> IetfLanguageCode.English.LS
 | 
			
		||||
        IetfLanguageCode.English.MG.code -> IetfLanguageCode.English.MG
 | 
			
		||||
        IetfLanguageCode.English.MH.code -> IetfLanguageCode.English.MH
 | 
			
		||||
        IetfLanguageCode.English.MO.code -> IetfLanguageCode.English.MO
 | 
			
		||||
        IetfLanguageCode.English.MP.code -> IetfLanguageCode.English.MP
 | 
			
		||||
        IetfLanguageCode.English.MS.code -> IetfLanguageCode.English.MS
 | 
			
		||||
        IetfLanguageCode.English.MT.code -> IetfLanguageCode.English.MT
 | 
			
		||||
        IetfLanguageCode.English.MU.code -> IetfLanguageCode.English.MU
 | 
			
		||||
        IetfLanguageCode.English.MW.code -> IetfLanguageCode.English.MW
 | 
			
		||||
        IetfLanguageCode.English.MY.code -> IetfLanguageCode.English.MY
 | 
			
		||||
        IetfLanguageCode.English.NA.code -> IetfLanguageCode.English.NA
 | 
			
		||||
        IetfLanguageCode.English.NF.code -> IetfLanguageCode.English.NF
 | 
			
		||||
        IetfLanguageCode.English.NG.code -> IetfLanguageCode.English.NG
 | 
			
		||||
        IetfLanguageCode.English.NL.code -> IetfLanguageCode.English.NL
 | 
			
		||||
        IetfLanguageCode.English.NR.code -> IetfLanguageCode.English.NR
 | 
			
		||||
        IetfLanguageCode.English.NU.code -> IetfLanguageCode.English.NU
 | 
			
		||||
        IetfLanguageCode.English.NZ.code -> IetfLanguageCode.English.NZ
 | 
			
		||||
        IetfLanguageCode.English.PG.code -> IetfLanguageCode.English.PG
 | 
			
		||||
        IetfLanguageCode.English.PH.code -> IetfLanguageCode.English.PH
 | 
			
		||||
        IetfLanguageCode.English.PK.code -> IetfLanguageCode.English.PK
 | 
			
		||||
        IetfLanguageCode.English.PN.code -> IetfLanguageCode.English.PN
 | 
			
		||||
        IetfLanguageCode.English.PR.code -> IetfLanguageCode.English.PR
 | 
			
		||||
        IetfLanguageCode.English.PW.code -> IetfLanguageCode.English.PW
 | 
			
		||||
        IetfLanguageCode.English.RW.code -> IetfLanguageCode.English.RW
 | 
			
		||||
        IetfLanguageCode.English.SB.code -> IetfLanguageCode.English.SB
 | 
			
		||||
        IetfLanguageCode.English.SC.code -> IetfLanguageCode.English.SC
 | 
			
		||||
        IetfLanguageCode.English.SD.code -> IetfLanguageCode.English.SD
 | 
			
		||||
        IetfLanguageCode.English.SE.code -> IetfLanguageCode.English.SE
 | 
			
		||||
        IetfLanguageCode.English.SG.code -> IetfLanguageCode.English.SG
 | 
			
		||||
        IetfLanguageCode.English.SH.code -> IetfLanguageCode.English.SH
 | 
			
		||||
        IetfLanguageCode.English.SI.code -> IetfLanguageCode.English.SI
 | 
			
		||||
        IetfLanguageCode.English.SL.code -> IetfLanguageCode.English.SL
 | 
			
		||||
        IetfLanguageCode.English.SS.code -> IetfLanguageCode.English.SS
 | 
			
		||||
        IetfLanguageCode.English.SX.code -> IetfLanguageCode.English.SX
 | 
			
		||||
        IetfLanguageCode.English.SZ.code -> IetfLanguageCode.English.SZ
 | 
			
		||||
        IetfLanguageCode.English.TC.code -> IetfLanguageCode.English.TC
 | 
			
		||||
        IetfLanguageCode.English.TK.code -> IetfLanguageCode.English.TK
 | 
			
		||||
        IetfLanguageCode.English.TO.code -> IetfLanguageCode.English.TO
 | 
			
		||||
        IetfLanguageCode.English.TT.code -> IetfLanguageCode.English.TT
 | 
			
		||||
        IetfLanguageCode.English.TV.code -> IetfLanguageCode.English.TV
 | 
			
		||||
        IetfLanguageCode.English.TZ.code -> IetfLanguageCode.English.TZ
 | 
			
		||||
        IetfLanguageCode.English.UG.code -> IetfLanguageCode.English.UG
 | 
			
		||||
        IetfLanguageCode.English.UM.code -> IetfLanguageCode.English.UM
 | 
			
		||||
        IetfLanguageCode.English.US.code -> IetfLanguageCode.English.US
 | 
			
		||||
        IetfLanguageCode.English.US.POSIX.code -> IetfLanguageCode.English.US.POSIX
 | 
			
		||||
        IetfLanguageCode.English.VC.code -> IetfLanguageCode.English.VC
 | 
			
		||||
        IetfLanguageCode.English.VG.code -> IetfLanguageCode.English.VG
 | 
			
		||||
        IetfLanguageCode.English.VI.code -> IetfLanguageCode.English.VI
 | 
			
		||||
        IetfLanguageCode.English.VU.code -> IetfLanguageCode.English.VU
 | 
			
		||||
        IetfLanguageCode.English.WS.code -> IetfLanguageCode.English.WS
 | 
			
		||||
        IetfLanguageCode.English.ZA.code -> IetfLanguageCode.English.ZA
 | 
			
		||||
        IetfLanguageCode.English.ZM.code -> IetfLanguageCode.English.ZM
 | 
			
		||||
        IetfLanguageCode.English.ZW.code -> IetfLanguageCode.English.ZW
 | 
			
		||||
        IetfLanguageCode.Esperanto.code -> IetfLanguageCode.Esperanto
 | 
			
		||||
        IetfLanguageCode.Esperanto.L001.code -> IetfLanguageCode.Esperanto.L001
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.code -> IetfLanguageCode.SpanishCastilian
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.L419.code -> IetfLanguageCode.SpanishCastilian.L419
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.AR.code -> IetfLanguageCode.SpanishCastilian.AR
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.BO.code -> IetfLanguageCode.SpanishCastilian.BO
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.BR.code -> IetfLanguageCode.SpanishCastilian.BR
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.BZ.code -> IetfLanguageCode.SpanishCastilian.BZ
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.CL.code -> IetfLanguageCode.SpanishCastilian.CL
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.CO.code -> IetfLanguageCode.SpanishCastilian.CO
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.CR.code -> IetfLanguageCode.SpanishCastilian.CR
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.CU.code -> IetfLanguageCode.SpanishCastilian.CU
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.DO.code -> IetfLanguageCode.SpanishCastilian.DO
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.EA.code -> IetfLanguageCode.SpanishCastilian.EA
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.EC.code -> IetfLanguageCode.SpanishCastilian.EC
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.ES.code -> IetfLanguageCode.SpanishCastilian.ES
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.GQ.code -> IetfLanguageCode.SpanishCastilian.GQ
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.GT.code -> IetfLanguageCode.SpanishCastilian.GT
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.HN.code -> IetfLanguageCode.SpanishCastilian.HN
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.IC.code -> IetfLanguageCode.SpanishCastilian.IC
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.MX.code -> IetfLanguageCode.SpanishCastilian.MX
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.NI.code -> IetfLanguageCode.SpanishCastilian.NI
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.PA.code -> IetfLanguageCode.SpanishCastilian.PA
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.PE.code -> IetfLanguageCode.SpanishCastilian.PE
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.PH.code -> IetfLanguageCode.SpanishCastilian.PH
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.PR.code -> IetfLanguageCode.SpanishCastilian.PR
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.PY.code -> IetfLanguageCode.SpanishCastilian.PY
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.SV.code -> IetfLanguageCode.SpanishCastilian.SV
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.US.code -> IetfLanguageCode.SpanishCastilian.US
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.UY.code -> IetfLanguageCode.SpanishCastilian.UY
 | 
			
		||||
        IetfLanguageCode.SpanishCastilian.VE.code -> IetfLanguageCode.SpanishCastilian.VE
 | 
			
		||||
        IetfLanguageCode.Estonian.code -> IetfLanguageCode.Estonian
 | 
			
		||||
        IetfLanguageCode.Estonian.EE.code -> IetfLanguageCode.Estonian.EE
 | 
			
		||||
        IetfLanguageCode.Basque.code -> IetfLanguageCode.Basque
 | 
			
		||||
        IetfLanguageCode.Basque.ES.code -> IetfLanguageCode.Basque.ES
 | 
			
		||||
        IetfLanguageCode.Persian.code -> IetfLanguageCode.Persian
 | 
			
		||||
        IetfLanguageCode.Persian.AF.code -> IetfLanguageCode.Persian.AF
 | 
			
		||||
        IetfLanguageCode.Persian.IR.code -> IetfLanguageCode.Persian.IR
 | 
			
		||||
        IetfLanguageCode.Fulah.code -> IetfLanguageCode.Fulah
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.code -> IetfLanguageCode.Fulah.Adlm
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.BF.code -> IetfLanguageCode.Fulah.Adlm.BF
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.CM.code -> IetfLanguageCode.Fulah.Adlm.CM
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.GH.code -> IetfLanguageCode.Fulah.Adlm.GH
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.GM.code -> IetfLanguageCode.Fulah.Adlm.GM
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.GN.code -> IetfLanguageCode.Fulah.Adlm.GN
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.GW.code -> IetfLanguageCode.Fulah.Adlm.GW
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.LR.code -> IetfLanguageCode.Fulah.Adlm.LR
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.MR.code -> IetfLanguageCode.Fulah.Adlm.MR
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.NE.code -> IetfLanguageCode.Fulah.Adlm.NE
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.NG.code -> IetfLanguageCode.Fulah.Adlm.NG
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.SL.code -> IetfLanguageCode.Fulah.Adlm.SL
 | 
			
		||||
        IetfLanguageCode.Fulah.Adlm.SN.code -> IetfLanguageCode.Fulah.Adlm.SN
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.code -> IetfLanguageCode.Fulah.Latn
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.BF.code -> IetfLanguageCode.Fulah.Latn.BF
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.CM.code -> IetfLanguageCode.Fulah.Latn.CM
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.GH.code -> IetfLanguageCode.Fulah.Latn.GH
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.GM.code -> IetfLanguageCode.Fulah.Latn.GM
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.GN.code -> IetfLanguageCode.Fulah.Latn.GN
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.GW.code -> IetfLanguageCode.Fulah.Latn.GW
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.LR.code -> IetfLanguageCode.Fulah.Latn.LR
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.MR.code -> IetfLanguageCode.Fulah.Latn.MR
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.NE.code -> IetfLanguageCode.Fulah.Latn.NE
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.NG.code -> IetfLanguageCode.Fulah.Latn.NG
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.SL.code -> IetfLanguageCode.Fulah.Latn.SL
 | 
			
		||||
        IetfLanguageCode.Fulah.Latn.SN.code -> IetfLanguageCode.Fulah.Latn.SN
 | 
			
		||||
        IetfLanguageCode.Finnish.code -> IetfLanguageCode.Finnish
 | 
			
		||||
        IetfLanguageCode.Finnish.FI.code -> IetfLanguageCode.Finnish.FI
 | 
			
		||||
        IetfLanguageCode.Fijian.code -> IetfLanguageCode.Fijian
 | 
			
		||||
        IetfLanguageCode.Faroese.code -> IetfLanguageCode.Faroese
 | 
			
		||||
        IetfLanguageCode.Faroese.DK.code -> IetfLanguageCode.Faroese.DK
 | 
			
		||||
        IetfLanguageCode.Faroese.FO.code -> IetfLanguageCode.Faroese.FO
 | 
			
		||||
        IetfLanguageCode.French.code -> IetfLanguageCode.French
 | 
			
		||||
        IetfLanguageCode.French.BE.code -> IetfLanguageCode.French.BE
 | 
			
		||||
        IetfLanguageCode.French.BF.code -> IetfLanguageCode.French.BF
 | 
			
		||||
        IetfLanguageCode.French.BI.code -> IetfLanguageCode.French.BI
 | 
			
		||||
        IetfLanguageCode.French.BJ.code -> IetfLanguageCode.French.BJ
 | 
			
		||||
        IetfLanguageCode.French.BL.code -> IetfLanguageCode.French.BL
 | 
			
		||||
        IetfLanguageCode.French.CA.code -> IetfLanguageCode.French.CA
 | 
			
		||||
        IetfLanguageCode.French.CD.code -> IetfLanguageCode.French.CD
 | 
			
		||||
        IetfLanguageCode.French.CF.code -> IetfLanguageCode.French.CF
 | 
			
		||||
        IetfLanguageCode.French.CG.code -> IetfLanguageCode.French.CG
 | 
			
		||||
        IetfLanguageCode.French.CH.code -> IetfLanguageCode.French.CH
 | 
			
		||||
        IetfLanguageCode.French.CI.code -> IetfLanguageCode.French.CI
 | 
			
		||||
        IetfLanguageCode.French.CM.code -> IetfLanguageCode.French.CM
 | 
			
		||||
        IetfLanguageCode.French.DJ.code -> IetfLanguageCode.French.DJ
 | 
			
		||||
        IetfLanguageCode.French.DZ.code -> IetfLanguageCode.French.DZ
 | 
			
		||||
        IetfLanguageCode.French.FR.code -> IetfLanguageCode.French.FR
 | 
			
		||||
        IetfLanguageCode.French.GA.code -> IetfLanguageCode.French.GA
 | 
			
		||||
        IetfLanguageCode.French.GF.code -> IetfLanguageCode.French.GF
 | 
			
		||||
        IetfLanguageCode.French.GN.code -> IetfLanguageCode.French.GN
 | 
			
		||||
        IetfLanguageCode.French.GP.code -> IetfLanguageCode.French.GP
 | 
			
		||||
        IetfLanguageCode.French.GQ.code -> IetfLanguageCode.French.GQ
 | 
			
		||||
        IetfLanguageCode.French.HT.code -> IetfLanguageCode.French.HT
 | 
			
		||||
        IetfLanguageCode.French.KM.code -> IetfLanguageCode.French.KM
 | 
			
		||||
        IetfLanguageCode.French.LU.code -> IetfLanguageCode.French.LU
 | 
			
		||||
        IetfLanguageCode.French.MA.code -> IetfLanguageCode.French.MA
 | 
			
		||||
        IetfLanguageCode.French.MC.code -> IetfLanguageCode.French.MC
 | 
			
		||||
        IetfLanguageCode.French.MF.code -> IetfLanguageCode.French.MF
 | 
			
		||||
        IetfLanguageCode.French.MG.code -> IetfLanguageCode.French.MG
 | 
			
		||||
        IetfLanguageCode.French.ML.code -> IetfLanguageCode.French.ML
 | 
			
		||||
        IetfLanguageCode.French.MQ.code -> IetfLanguageCode.French.MQ
 | 
			
		||||
        IetfLanguageCode.French.MR.code -> IetfLanguageCode.French.MR
 | 
			
		||||
        IetfLanguageCode.French.MU.code -> IetfLanguageCode.French.MU
 | 
			
		||||
        IetfLanguageCode.French.NC.code -> IetfLanguageCode.French.NC
 | 
			
		||||
        IetfLanguageCode.French.NE.code -> IetfLanguageCode.French.NE
 | 
			
		||||
        IetfLanguageCode.French.PF.code -> IetfLanguageCode.French.PF
 | 
			
		||||
        IetfLanguageCode.French.PM.code -> IetfLanguageCode.French.PM
 | 
			
		||||
        IetfLanguageCode.French.RE.code -> IetfLanguageCode.French.RE
 | 
			
		||||
        IetfLanguageCode.French.RW.code -> IetfLanguageCode.French.RW
 | 
			
		||||
        IetfLanguageCode.French.SC.code -> IetfLanguageCode.French.SC
 | 
			
		||||
        IetfLanguageCode.French.SN.code -> IetfLanguageCode.French.SN
 | 
			
		||||
        IetfLanguageCode.French.SY.code -> IetfLanguageCode.French.SY
 | 
			
		||||
        IetfLanguageCode.French.TD.code -> IetfLanguageCode.French.TD
 | 
			
		||||
        IetfLanguageCode.French.TG.code -> IetfLanguageCode.French.TG
 | 
			
		||||
        IetfLanguageCode.French.TN.code -> IetfLanguageCode.French.TN
 | 
			
		||||
        IetfLanguageCode.French.VU.code -> IetfLanguageCode.French.VU
 | 
			
		||||
        IetfLanguageCode.French.WF.code -> IetfLanguageCode.French.WF
 | 
			
		||||
        IetfLanguageCode.French.YT.code -> IetfLanguageCode.French.YT
 | 
			
		||||
        IetfLanguageCode.WesternFrisian.code -> IetfLanguageCode.WesternFrisian
 | 
			
		||||
        IetfLanguageCode.WesternFrisian.NL.code -> IetfLanguageCode.WesternFrisian.NL
 | 
			
		||||
        IetfLanguageCode.Irish.code -> IetfLanguageCode.Irish
 | 
			
		||||
        IetfLanguageCode.Irish.GB.code -> IetfLanguageCode.Irish.GB
 | 
			
		||||
        IetfLanguageCode.Irish.IE.code -> IetfLanguageCode.Irish.IE
 | 
			
		||||
        IetfLanguageCode.GaelicScottishGaelic.code -> IetfLanguageCode.GaelicScottishGaelic
 | 
			
		||||
        IetfLanguageCode.GaelicScottishGaelic.GB.code -> IetfLanguageCode.GaelicScottishGaelic.GB
 | 
			
		||||
        IetfLanguageCode.Galician.code -> IetfLanguageCode.Galician
 | 
			
		||||
        IetfLanguageCode.Galician.ES.code -> IetfLanguageCode.Galician.ES
 | 
			
		||||
        IetfLanguageCode.Guarani.code -> IetfLanguageCode.Guarani
 | 
			
		||||
        IetfLanguageCode.Gujarati.code -> IetfLanguageCode.Gujarati
 | 
			
		||||
        IetfLanguageCode.Gujarati.IN.code -> IetfLanguageCode.Gujarati.IN
 | 
			
		||||
        IetfLanguageCode.Manx.code -> IetfLanguageCode.Manx
 | 
			
		||||
        IetfLanguageCode.Manx.IM.code -> IetfLanguageCode.Manx.IM
 | 
			
		||||
        IetfLanguageCode.Hausa.code -> IetfLanguageCode.Hausa
 | 
			
		||||
        IetfLanguageCode.Hausa.GH.code -> IetfLanguageCode.Hausa.GH
 | 
			
		||||
        IetfLanguageCode.Hausa.NE.code -> IetfLanguageCode.Hausa.NE
 | 
			
		||||
        IetfLanguageCode.Hausa.NG.code -> IetfLanguageCode.Hausa.NG
 | 
			
		||||
        IetfLanguageCode.Hebrew.code -> IetfLanguageCode.Hebrew
 | 
			
		||||
        IetfLanguageCode.Hebrew.IL.code -> IetfLanguageCode.Hebrew.IL
 | 
			
		||||
        IetfLanguageCode.Hindi.code -> IetfLanguageCode.Hindi
 | 
			
		||||
        IetfLanguageCode.Hindi.IN.code -> IetfLanguageCode.Hindi.IN
 | 
			
		||||
        IetfLanguageCode.HiriMotu.code -> IetfLanguageCode.HiriMotu
 | 
			
		||||
        IetfLanguageCode.Croatian.code -> IetfLanguageCode.Croatian
 | 
			
		||||
        IetfLanguageCode.Croatian.BA.code -> IetfLanguageCode.Croatian.BA
 | 
			
		||||
        IetfLanguageCode.Croatian.HR.code -> IetfLanguageCode.Croatian.HR
 | 
			
		||||
        IetfLanguageCode.HaitianHaitianCreole.code -> IetfLanguageCode.HaitianHaitianCreole
 | 
			
		||||
        IetfLanguageCode.Hungarian.code -> IetfLanguageCode.Hungarian
 | 
			
		||||
        IetfLanguageCode.Hungarian.HU.code -> IetfLanguageCode.Hungarian.HU
 | 
			
		||||
        IetfLanguageCode.Armenian.code -> IetfLanguageCode.Armenian
 | 
			
		||||
        IetfLanguageCode.Armenian.AM.code -> IetfLanguageCode.Armenian.AM
 | 
			
		||||
        IetfLanguageCode.Herero.code -> IetfLanguageCode.Herero
 | 
			
		||||
        IetfLanguageCode.InterlinguaInternationalAuxiliaryLanguageAssociation.code -> IetfLanguageCode.InterlinguaInternationalAuxiliaryLanguageAssociation
 | 
			
		||||
        IetfLanguageCode.InterlinguaInternationalAuxiliaryLanguageAssociation.L001.code -> IetfLanguageCode.InterlinguaInternationalAuxiliaryLanguageAssociation.L001
 | 
			
		||||
        IetfLanguageCode.Indonesian.code -> IetfLanguageCode.Indonesian
 | 
			
		||||
        IetfLanguageCode.Indonesian.ID.code -> IetfLanguageCode.Indonesian.ID
 | 
			
		||||
        IetfLanguageCode.InterlingueOccidental.code -> IetfLanguageCode.InterlingueOccidental
 | 
			
		||||
        IetfLanguageCode.Igbo.code -> IetfLanguageCode.Igbo
 | 
			
		||||
        IetfLanguageCode.Igbo.NG.code -> IetfLanguageCode.Igbo.NG
 | 
			
		||||
        IetfLanguageCode.SichuanYiNuosu.code -> IetfLanguageCode.SichuanYiNuosu
 | 
			
		||||
        IetfLanguageCode.SichuanYiNuosu.CN.code -> IetfLanguageCode.SichuanYiNuosu.CN
 | 
			
		||||
        IetfLanguageCode.Inupiaq.code -> IetfLanguageCode.Inupiaq
 | 
			
		||||
        IetfLanguageCode.Ido.code -> IetfLanguageCode.Ido
 | 
			
		||||
        IetfLanguageCode.Icelandic.code -> IetfLanguageCode.Icelandic
 | 
			
		||||
        IetfLanguageCode.Icelandic.IS.code -> IetfLanguageCode.Icelandic.IS
 | 
			
		||||
        IetfLanguageCode.Italian.code -> IetfLanguageCode.Italian
 | 
			
		||||
        IetfLanguageCode.Italian.CH.code -> IetfLanguageCode.Italian.CH
 | 
			
		||||
        IetfLanguageCode.Italian.IT.code -> IetfLanguageCode.Italian.IT
 | 
			
		||||
        IetfLanguageCode.Italian.SM.code -> IetfLanguageCode.Italian.SM
 | 
			
		||||
        IetfLanguageCode.Italian.VA.code -> IetfLanguageCode.Italian.VA
 | 
			
		||||
        IetfLanguageCode.Inuktitut.code -> IetfLanguageCode.Inuktitut
 | 
			
		||||
        IetfLanguageCode.Japanese.code -> IetfLanguageCode.Japanese
 | 
			
		||||
        IetfLanguageCode.Japanese.JP.code -> IetfLanguageCode.Japanese.JP
 | 
			
		||||
        IetfLanguageCode.Javanese.code -> IetfLanguageCode.Javanese
 | 
			
		||||
        IetfLanguageCode.Javanese.ID.code -> IetfLanguageCode.Javanese.ID
 | 
			
		||||
        IetfLanguageCode.Georgian.code -> IetfLanguageCode.Georgian
 | 
			
		||||
        IetfLanguageCode.Georgian.GE.code -> IetfLanguageCode.Georgian.GE
 | 
			
		||||
        IetfLanguageCode.Kongo.code -> IetfLanguageCode.Kongo
 | 
			
		||||
        IetfLanguageCode.KikuyuGikuyu.code -> IetfLanguageCode.KikuyuGikuyu
 | 
			
		||||
        IetfLanguageCode.KikuyuGikuyu.KE.code -> IetfLanguageCode.KikuyuGikuyu.KE
 | 
			
		||||
        IetfLanguageCode.KuanyamaKwanyama.code -> IetfLanguageCode.KuanyamaKwanyama
 | 
			
		||||
        IetfLanguageCode.Kazakh.code -> IetfLanguageCode.Kazakh
 | 
			
		||||
        IetfLanguageCode.Kazakh.KZ.code -> IetfLanguageCode.Kazakh.KZ
 | 
			
		||||
        IetfLanguageCode.KalaallisutGreenlandic.code -> IetfLanguageCode.KalaallisutGreenlandic
 | 
			
		||||
        IetfLanguageCode.KalaallisutGreenlandic.GL.code -> IetfLanguageCode.KalaallisutGreenlandic.GL
 | 
			
		||||
        IetfLanguageCode.CentralKhmer.code -> IetfLanguageCode.CentralKhmer
 | 
			
		||||
        IetfLanguageCode.CentralKhmer.KH.code -> IetfLanguageCode.CentralKhmer.KH
 | 
			
		||||
        IetfLanguageCode.Kannada.code -> IetfLanguageCode.Kannada
 | 
			
		||||
        IetfLanguageCode.Kannada.IN.code -> IetfLanguageCode.Kannada.IN
 | 
			
		||||
        IetfLanguageCode.Korean.code -> IetfLanguageCode.Korean
 | 
			
		||||
        IetfLanguageCode.Korean.KP.code -> IetfLanguageCode.Korean.KP
 | 
			
		||||
        IetfLanguageCode.Korean.KR.code -> IetfLanguageCode.Korean.KR
 | 
			
		||||
        IetfLanguageCode.Kanuri.code -> IetfLanguageCode.Kanuri
 | 
			
		||||
        IetfLanguageCode.Kashmiri.code -> IetfLanguageCode.Kashmiri
 | 
			
		||||
        IetfLanguageCode.Kashmiri.Arab.code -> IetfLanguageCode.Kashmiri.Arab
 | 
			
		||||
        IetfLanguageCode.Kashmiri.Arab.IN.code -> IetfLanguageCode.Kashmiri.Arab.IN
 | 
			
		||||
        IetfLanguageCode.Kurdish.code -> IetfLanguageCode.Kurdish
 | 
			
		||||
        IetfLanguageCode.Kurdish.TR.code -> IetfLanguageCode.Kurdish.TR
 | 
			
		||||
        IetfLanguageCode.Komi.code -> IetfLanguageCode.Komi
 | 
			
		||||
        IetfLanguageCode.Cornish.code -> IetfLanguageCode.Cornish
 | 
			
		||||
        IetfLanguageCode.Cornish.GB.code -> IetfLanguageCode.Cornish.GB
 | 
			
		||||
        IetfLanguageCode.KirghizKyrgyz.code -> IetfLanguageCode.KirghizKyrgyz
 | 
			
		||||
        IetfLanguageCode.KirghizKyrgyz.KG.code -> IetfLanguageCode.KirghizKyrgyz.KG
 | 
			
		||||
        IetfLanguageCode.Latin.code -> IetfLanguageCode.Latin
 | 
			
		||||
        IetfLanguageCode.LuxembourgishLetzeburgesch.code -> IetfLanguageCode.LuxembourgishLetzeburgesch
 | 
			
		||||
        IetfLanguageCode.LuxembourgishLetzeburgesch.LU.code -> IetfLanguageCode.LuxembourgishLetzeburgesch.LU
 | 
			
		||||
        IetfLanguageCode.Ganda.code -> IetfLanguageCode.Ganda
 | 
			
		||||
        IetfLanguageCode.Ganda.UG.code -> IetfLanguageCode.Ganda.UG
 | 
			
		||||
        IetfLanguageCode.LimburganLimburgerLimburgish.code -> IetfLanguageCode.LimburganLimburgerLimburgish
 | 
			
		||||
        IetfLanguageCode.Lingala.code -> IetfLanguageCode.Lingala
 | 
			
		||||
        IetfLanguageCode.Lingala.AO.code -> IetfLanguageCode.Lingala.AO
 | 
			
		||||
        IetfLanguageCode.Lingala.CD.code -> IetfLanguageCode.Lingala.CD
 | 
			
		||||
        IetfLanguageCode.Lingala.CF.code -> IetfLanguageCode.Lingala.CF
 | 
			
		||||
        IetfLanguageCode.Lingala.CG.code -> IetfLanguageCode.Lingala.CG
 | 
			
		||||
        IetfLanguageCode.Lao.code -> IetfLanguageCode.Lao
 | 
			
		||||
        IetfLanguageCode.Lao.LA.code -> IetfLanguageCode.Lao.LA
 | 
			
		||||
        IetfLanguageCode.Lithuanian.code -> IetfLanguageCode.Lithuanian
 | 
			
		||||
        IetfLanguageCode.Lithuanian.LT.code -> IetfLanguageCode.Lithuanian.LT
 | 
			
		||||
        IetfLanguageCode.LubaKatanga.code -> IetfLanguageCode.LubaKatanga
 | 
			
		||||
        IetfLanguageCode.LubaKatanga.CD.code -> IetfLanguageCode.LubaKatanga.CD
 | 
			
		||||
        IetfLanguageCode.Latvian.code -> IetfLanguageCode.Latvian
 | 
			
		||||
        IetfLanguageCode.Latvian.LV.code -> IetfLanguageCode.Latvian.LV
 | 
			
		||||
        IetfLanguageCode.Malagasy.code -> IetfLanguageCode.Malagasy
 | 
			
		||||
        IetfLanguageCode.Malagasy.MG.code -> IetfLanguageCode.Malagasy.MG
 | 
			
		||||
        IetfLanguageCode.Marshallese.code -> IetfLanguageCode.Marshallese
 | 
			
		||||
        IetfLanguageCode.Maori.code -> IetfLanguageCode.Maori
 | 
			
		||||
        IetfLanguageCode.Maori.NZ.code -> IetfLanguageCode.Maori.NZ
 | 
			
		||||
        IetfLanguageCode.Macedonian.code -> IetfLanguageCode.Macedonian
 | 
			
		||||
        IetfLanguageCode.Macedonian.MK.code -> IetfLanguageCode.Macedonian.MK
 | 
			
		||||
        IetfLanguageCode.Malayalam.code -> IetfLanguageCode.Malayalam
 | 
			
		||||
        IetfLanguageCode.Malayalam.IN.code -> IetfLanguageCode.Malayalam.IN
 | 
			
		||||
        IetfLanguageCode.Mongolian.code -> IetfLanguageCode.Mongolian
 | 
			
		||||
        IetfLanguageCode.Mongolian.MN.code -> IetfLanguageCode.Mongolian.MN
 | 
			
		||||
        IetfLanguageCode.Marathi.code -> IetfLanguageCode.Marathi
 | 
			
		||||
        IetfLanguageCode.Marathi.IN.code -> IetfLanguageCode.Marathi.IN
 | 
			
		||||
        IetfLanguageCode.Malay.code -> IetfLanguageCode.Malay
 | 
			
		||||
        IetfLanguageCode.Malay.BN.code -> IetfLanguageCode.Malay.BN
 | 
			
		||||
        IetfLanguageCode.Malay.ID.code -> IetfLanguageCode.Malay.ID
 | 
			
		||||
        IetfLanguageCode.Malay.MY.code -> IetfLanguageCode.Malay.MY
 | 
			
		||||
        IetfLanguageCode.Malay.SG.code -> IetfLanguageCode.Malay.SG
 | 
			
		||||
        IetfLanguageCode.Maltese.code -> IetfLanguageCode.Maltese
 | 
			
		||||
        IetfLanguageCode.Maltese.MT.code -> IetfLanguageCode.Maltese.MT
 | 
			
		||||
        IetfLanguageCode.Burmese.code -> IetfLanguageCode.Burmese
 | 
			
		||||
        IetfLanguageCode.Burmese.MM.code -> IetfLanguageCode.Burmese.MM
 | 
			
		||||
        IetfLanguageCode.Nauru.code -> IetfLanguageCode.Nauru
 | 
			
		||||
        IetfLanguageCode.BokmalNorwegianNorwegianBokmal.code -> IetfLanguageCode.BokmalNorwegianNorwegianBokmal
 | 
			
		||||
        IetfLanguageCode.BokmalNorwegianNorwegianBokmal.NO.code -> IetfLanguageCode.BokmalNorwegianNorwegianBokmal.NO
 | 
			
		||||
        IetfLanguageCode.BokmalNorwegianNorwegianBokmal.SJ.code -> IetfLanguageCode.BokmalNorwegianNorwegianBokmal.SJ
 | 
			
		||||
        IetfLanguageCode.NdebeleNorthNorthNdebele.code -> IetfLanguageCode.NdebeleNorthNorthNdebele
 | 
			
		||||
        IetfLanguageCode.NdebeleNorthNorthNdebele.ZW.code -> IetfLanguageCode.NdebeleNorthNorthNdebele.ZW
 | 
			
		||||
        IetfLanguageCode.Nepali.code -> IetfLanguageCode.Nepali
 | 
			
		||||
        IetfLanguageCode.Nepali.IN.code -> IetfLanguageCode.Nepali.IN
 | 
			
		||||
        IetfLanguageCode.Nepali.NP.code -> IetfLanguageCode.Nepali.NP
 | 
			
		||||
        IetfLanguageCode.Ndonga.code -> IetfLanguageCode.Ndonga
 | 
			
		||||
        IetfLanguageCode.DutchFlemish.code -> IetfLanguageCode.DutchFlemish
 | 
			
		||||
        IetfLanguageCode.DutchFlemish.AW.code -> IetfLanguageCode.DutchFlemish.AW
 | 
			
		||||
        IetfLanguageCode.DutchFlemish.BE.code -> IetfLanguageCode.DutchFlemish.BE
 | 
			
		||||
        IetfLanguageCode.DutchFlemish.BQ.code -> IetfLanguageCode.DutchFlemish.BQ
 | 
			
		||||
        IetfLanguageCode.DutchFlemish.CW.code -> IetfLanguageCode.DutchFlemish.CW
 | 
			
		||||
        IetfLanguageCode.DutchFlemish.NL.code -> IetfLanguageCode.DutchFlemish.NL
 | 
			
		||||
        IetfLanguageCode.DutchFlemish.SR.code -> IetfLanguageCode.DutchFlemish.SR
 | 
			
		||||
        IetfLanguageCode.DutchFlemish.SX.code -> IetfLanguageCode.DutchFlemish.SX
 | 
			
		||||
        IetfLanguageCode.NorwegianNynorskNynorskNorwegian.code -> IetfLanguageCode.NorwegianNynorskNynorskNorwegian
 | 
			
		||||
        IetfLanguageCode.NorwegianNynorskNynorskNorwegian.NO.code -> IetfLanguageCode.NorwegianNynorskNynorskNorwegian.NO
 | 
			
		||||
        IetfLanguageCode.Norwegian.code -> IetfLanguageCode.Norwegian
 | 
			
		||||
        IetfLanguageCode.NdebeleSouthSouthNdebele.code -> IetfLanguageCode.NdebeleSouthSouthNdebele
 | 
			
		||||
        IetfLanguageCode.NavajoNavaho.code -> IetfLanguageCode.NavajoNavaho
 | 
			
		||||
        IetfLanguageCode.ChichewaChewaNyanja.code -> IetfLanguageCode.ChichewaChewaNyanja
 | 
			
		||||
        IetfLanguageCode.OccitanPost1500.code -> IetfLanguageCode.OccitanPost1500
 | 
			
		||||
        IetfLanguageCode.Ojibwa.code -> IetfLanguageCode.Ojibwa
 | 
			
		||||
        IetfLanguageCode.Oromo.code -> IetfLanguageCode.Oromo
 | 
			
		||||
        IetfLanguageCode.Oromo.ET.code -> IetfLanguageCode.Oromo.ET
 | 
			
		||||
        IetfLanguageCode.Oromo.KE.code -> IetfLanguageCode.Oromo.KE
 | 
			
		||||
        IetfLanguageCode.Oriya.code -> IetfLanguageCode.Oriya
 | 
			
		||||
        IetfLanguageCode.Oriya.IN.code -> IetfLanguageCode.Oriya.IN
 | 
			
		||||
        IetfLanguageCode.OssetianOssetic.code -> IetfLanguageCode.OssetianOssetic
 | 
			
		||||
        IetfLanguageCode.OssetianOssetic.GE.code -> IetfLanguageCode.OssetianOssetic.GE
 | 
			
		||||
        IetfLanguageCode.OssetianOssetic.RU.code -> IetfLanguageCode.OssetianOssetic.RU
 | 
			
		||||
        IetfLanguageCode.PanjabiPunjabi.code -> IetfLanguageCode.PanjabiPunjabi
 | 
			
		||||
        IetfLanguageCode.PanjabiPunjabi.Arab.code -> IetfLanguageCode.PanjabiPunjabi.Arab
 | 
			
		||||
        IetfLanguageCode.PanjabiPunjabi.Arab.PK.code -> IetfLanguageCode.PanjabiPunjabi.Arab.PK
 | 
			
		||||
        IetfLanguageCode.PanjabiPunjabi.Guru.code -> IetfLanguageCode.PanjabiPunjabi.Guru
 | 
			
		||||
        IetfLanguageCode.PanjabiPunjabi.Guru.IN.code -> IetfLanguageCode.PanjabiPunjabi.Guru.IN
 | 
			
		||||
        IetfLanguageCode.Pali.code -> IetfLanguageCode.Pali
 | 
			
		||||
        IetfLanguageCode.Polish.code -> IetfLanguageCode.Polish
 | 
			
		||||
        IetfLanguageCode.Polish.PL.code -> IetfLanguageCode.Polish.PL
 | 
			
		||||
        IetfLanguageCode.PushtoPashto.code -> IetfLanguageCode.PushtoPashto
 | 
			
		||||
        IetfLanguageCode.PushtoPashto.AF.code -> IetfLanguageCode.PushtoPashto.AF
 | 
			
		||||
        IetfLanguageCode.PushtoPashto.PK.code -> IetfLanguageCode.PushtoPashto.PK
 | 
			
		||||
        IetfLanguageCode.Portuguese.code -> IetfLanguageCode.Portuguese
 | 
			
		||||
        IetfLanguageCode.Portuguese.AO.code -> IetfLanguageCode.Portuguese.AO
 | 
			
		||||
        IetfLanguageCode.Portuguese.BR.code -> IetfLanguageCode.Portuguese.BR
 | 
			
		||||
        IetfLanguageCode.Portuguese.CH.code -> IetfLanguageCode.Portuguese.CH
 | 
			
		||||
        IetfLanguageCode.Portuguese.CV.code -> IetfLanguageCode.Portuguese.CV
 | 
			
		||||
        IetfLanguageCode.Portuguese.GQ.code -> IetfLanguageCode.Portuguese.GQ
 | 
			
		||||
        IetfLanguageCode.Portuguese.GW.code -> IetfLanguageCode.Portuguese.GW
 | 
			
		||||
        IetfLanguageCode.Portuguese.LU.code -> IetfLanguageCode.Portuguese.LU
 | 
			
		||||
        IetfLanguageCode.Portuguese.MO.code -> IetfLanguageCode.Portuguese.MO
 | 
			
		||||
        IetfLanguageCode.Portuguese.MZ.code -> IetfLanguageCode.Portuguese.MZ
 | 
			
		||||
        IetfLanguageCode.Portuguese.PT.code -> IetfLanguageCode.Portuguese.PT
 | 
			
		||||
        IetfLanguageCode.Portuguese.ST.code -> IetfLanguageCode.Portuguese.ST
 | 
			
		||||
        IetfLanguageCode.Portuguese.TL.code -> IetfLanguageCode.Portuguese.TL
 | 
			
		||||
        IetfLanguageCode.Quechua.code -> IetfLanguageCode.Quechua
 | 
			
		||||
        IetfLanguageCode.Quechua.BO.code -> IetfLanguageCode.Quechua.BO
 | 
			
		||||
        IetfLanguageCode.Quechua.EC.code -> IetfLanguageCode.Quechua.EC
 | 
			
		||||
        IetfLanguageCode.Quechua.PE.code -> IetfLanguageCode.Quechua.PE
 | 
			
		||||
        IetfLanguageCode.Romansh.code -> IetfLanguageCode.Romansh
 | 
			
		||||
        IetfLanguageCode.Romansh.CH.code -> IetfLanguageCode.Romansh.CH
 | 
			
		||||
        IetfLanguageCode.Rundi.code -> IetfLanguageCode.Rundi
 | 
			
		||||
        IetfLanguageCode.Rundi.BI.code -> IetfLanguageCode.Rundi.BI
 | 
			
		||||
        IetfLanguageCode.RomanianMoldavianMoldovan.code -> IetfLanguageCode.RomanianMoldavianMoldovan
 | 
			
		||||
        IetfLanguageCode.RomanianMoldavianMoldovan.MD.code -> IetfLanguageCode.RomanianMoldavianMoldovan.MD
 | 
			
		||||
        IetfLanguageCode.RomanianMoldavianMoldovan.RO.code -> IetfLanguageCode.RomanianMoldavianMoldovan.RO
 | 
			
		||||
        IetfLanguageCode.Russian.code -> IetfLanguageCode.Russian
 | 
			
		||||
        IetfLanguageCode.Russian.BY.code -> IetfLanguageCode.Russian.BY
 | 
			
		||||
        IetfLanguageCode.Russian.KG.code -> IetfLanguageCode.Russian.KG
 | 
			
		||||
        IetfLanguageCode.Russian.KZ.code -> IetfLanguageCode.Russian.KZ
 | 
			
		||||
        IetfLanguageCode.Russian.MD.code -> IetfLanguageCode.Russian.MD
 | 
			
		||||
        IetfLanguageCode.Russian.RU.code -> IetfLanguageCode.Russian.RU
 | 
			
		||||
        IetfLanguageCode.Russian.UA.code -> IetfLanguageCode.Russian.UA
 | 
			
		||||
        IetfLanguageCode.Kinyarwanda.code -> IetfLanguageCode.Kinyarwanda
 | 
			
		||||
        IetfLanguageCode.Kinyarwanda.RW.code -> IetfLanguageCode.Kinyarwanda.RW
 | 
			
		||||
        IetfLanguageCode.Sanskrit.code -> IetfLanguageCode.Sanskrit
 | 
			
		||||
        IetfLanguageCode.Sardinian.code -> IetfLanguageCode.Sardinian
 | 
			
		||||
        IetfLanguageCode.Sindhi.code -> IetfLanguageCode.Sindhi
 | 
			
		||||
        IetfLanguageCode.Sindhi.Arab.code -> IetfLanguageCode.Sindhi.Arab
 | 
			
		||||
        IetfLanguageCode.Sindhi.Arab.PK.code -> IetfLanguageCode.Sindhi.Arab.PK
 | 
			
		||||
        IetfLanguageCode.Sindhi.Deva.code -> IetfLanguageCode.Sindhi.Deva
 | 
			
		||||
        IetfLanguageCode.Sindhi.Deva.IN.code -> IetfLanguageCode.Sindhi.Deva.IN
 | 
			
		||||
        IetfLanguageCode.NorthernSami.code -> IetfLanguageCode.NorthernSami
 | 
			
		||||
        IetfLanguageCode.NorthernSami.FI.code -> IetfLanguageCode.NorthernSami.FI
 | 
			
		||||
        IetfLanguageCode.NorthernSami.NO.code -> IetfLanguageCode.NorthernSami.NO
 | 
			
		||||
        IetfLanguageCode.NorthernSami.SE.code -> IetfLanguageCode.NorthernSami.SE
 | 
			
		||||
        IetfLanguageCode.Sango.code -> IetfLanguageCode.Sango
 | 
			
		||||
        IetfLanguageCode.Sango.CF.code -> IetfLanguageCode.Sango.CF
 | 
			
		||||
        IetfLanguageCode.SinhalaSinhalese.code -> IetfLanguageCode.SinhalaSinhalese
 | 
			
		||||
        IetfLanguageCode.SinhalaSinhalese.LK.code -> IetfLanguageCode.SinhalaSinhalese.LK
 | 
			
		||||
        IetfLanguageCode.Slovak.code -> IetfLanguageCode.Slovak
 | 
			
		||||
        IetfLanguageCode.Slovak.SK.code -> IetfLanguageCode.Slovak.SK
 | 
			
		||||
        IetfLanguageCode.Slovenian.code -> IetfLanguageCode.Slovenian
 | 
			
		||||
        IetfLanguageCode.Slovenian.SI.code -> IetfLanguageCode.Slovenian.SI
 | 
			
		||||
        IetfLanguageCode.Samoan.code -> IetfLanguageCode.Samoan
 | 
			
		||||
        IetfLanguageCode.Shona.code -> IetfLanguageCode.Shona
 | 
			
		||||
        IetfLanguageCode.Shona.ZW.code -> IetfLanguageCode.Shona.ZW
 | 
			
		||||
        IetfLanguageCode.Somali.code -> IetfLanguageCode.Somali
 | 
			
		||||
        IetfLanguageCode.Somali.DJ.code -> IetfLanguageCode.Somali.DJ
 | 
			
		||||
        IetfLanguageCode.Somali.ET.code -> IetfLanguageCode.Somali.ET
 | 
			
		||||
        IetfLanguageCode.Somali.KE.code -> IetfLanguageCode.Somali.KE
 | 
			
		||||
        IetfLanguageCode.Somali.SO.code -> IetfLanguageCode.Somali.SO
 | 
			
		||||
        IetfLanguageCode.Albanian.code -> IetfLanguageCode.Albanian
 | 
			
		||||
        IetfLanguageCode.Albanian.AL.code -> IetfLanguageCode.Albanian.AL
 | 
			
		||||
        IetfLanguageCode.Albanian.MK.code -> IetfLanguageCode.Albanian.MK
 | 
			
		||||
        IetfLanguageCode.Albanian.XK.code -> IetfLanguageCode.Albanian.XK
 | 
			
		||||
        IetfLanguageCode.Serbian.code -> IetfLanguageCode.Serbian
 | 
			
		||||
        IetfLanguageCode.Serbian.Cyrl.code -> IetfLanguageCode.Serbian.Cyrl
 | 
			
		||||
        IetfLanguageCode.Serbian.Cyrl.BA.code -> IetfLanguageCode.Serbian.Cyrl.BA
 | 
			
		||||
        IetfLanguageCode.Serbian.Cyrl.ME.code -> IetfLanguageCode.Serbian.Cyrl.ME
 | 
			
		||||
        IetfLanguageCode.Serbian.Cyrl.RS.code -> IetfLanguageCode.Serbian.Cyrl.RS
 | 
			
		||||
        IetfLanguageCode.Serbian.Cyrl.XK.code -> IetfLanguageCode.Serbian.Cyrl.XK
 | 
			
		||||
        IetfLanguageCode.Serbian.Latn.code -> IetfLanguageCode.Serbian.Latn
 | 
			
		||||
        IetfLanguageCode.Serbian.Latn.BA.code -> IetfLanguageCode.Serbian.Latn.BA
 | 
			
		||||
        IetfLanguageCode.Serbian.Latn.ME.code -> IetfLanguageCode.Serbian.Latn.ME
 | 
			
		||||
        IetfLanguageCode.Serbian.Latn.RS.code -> IetfLanguageCode.Serbian.Latn.RS
 | 
			
		||||
        IetfLanguageCode.Serbian.Latn.XK.code -> IetfLanguageCode.Serbian.Latn.XK
 | 
			
		||||
        IetfLanguageCode.Swati.code -> IetfLanguageCode.Swati
 | 
			
		||||
        IetfLanguageCode.SothoSouthern.code -> IetfLanguageCode.SothoSouthern
 | 
			
		||||
        IetfLanguageCode.Sundanese.code -> IetfLanguageCode.Sundanese
 | 
			
		||||
        IetfLanguageCode.Sundanese.Latn.code -> IetfLanguageCode.Sundanese.Latn
 | 
			
		||||
        IetfLanguageCode.Sundanese.Latn.ID.code -> IetfLanguageCode.Sundanese.Latn.ID
 | 
			
		||||
        IetfLanguageCode.Swedish.code -> IetfLanguageCode.Swedish
 | 
			
		||||
        IetfLanguageCode.Swedish.AX.code -> IetfLanguageCode.Swedish.AX
 | 
			
		||||
        IetfLanguageCode.Swedish.FI.code -> IetfLanguageCode.Swedish.FI
 | 
			
		||||
        IetfLanguageCode.Swedish.SE.code -> IetfLanguageCode.Swedish.SE
 | 
			
		||||
        IetfLanguageCode.Swahili.code -> IetfLanguageCode.Swahili
 | 
			
		||||
        IetfLanguageCode.Swahili.CD.code -> IetfLanguageCode.Swahili.CD
 | 
			
		||||
        IetfLanguageCode.Swahili.KE.code -> IetfLanguageCode.Swahili.KE
 | 
			
		||||
        IetfLanguageCode.Swahili.TZ.code -> IetfLanguageCode.Swahili.TZ
 | 
			
		||||
        IetfLanguageCode.Swahili.UG.code -> IetfLanguageCode.Swahili.UG
 | 
			
		||||
        IetfLanguageCode.Tamil.code -> IetfLanguageCode.Tamil
 | 
			
		||||
        IetfLanguageCode.Tamil.IN.code -> IetfLanguageCode.Tamil.IN
 | 
			
		||||
        IetfLanguageCode.Tamil.LK.code -> IetfLanguageCode.Tamil.LK
 | 
			
		||||
        IetfLanguageCode.Tamil.MY.code -> IetfLanguageCode.Tamil.MY
 | 
			
		||||
        IetfLanguageCode.Tamil.SG.code -> IetfLanguageCode.Tamil.SG
 | 
			
		||||
        IetfLanguageCode.Telugu.code -> IetfLanguageCode.Telugu
 | 
			
		||||
        IetfLanguageCode.Telugu.IN.code -> IetfLanguageCode.Telugu.IN
 | 
			
		||||
        IetfLanguageCode.Tajik.code -> IetfLanguageCode.Tajik
 | 
			
		||||
        IetfLanguageCode.Tajik.TJ.code -> IetfLanguageCode.Tajik.TJ
 | 
			
		||||
        IetfLanguageCode.Thai.code -> IetfLanguageCode.Thai
 | 
			
		||||
        IetfLanguageCode.Thai.TH.code -> IetfLanguageCode.Thai.TH
 | 
			
		||||
        IetfLanguageCode.Tigrinya.code -> IetfLanguageCode.Tigrinya
 | 
			
		||||
        IetfLanguageCode.Tigrinya.ER.code -> IetfLanguageCode.Tigrinya.ER
 | 
			
		||||
        IetfLanguageCode.Tigrinya.ET.code -> IetfLanguageCode.Tigrinya.ET
 | 
			
		||||
        IetfLanguageCode.Turkmen.code -> IetfLanguageCode.Turkmen
 | 
			
		||||
        IetfLanguageCode.Turkmen.TM.code -> IetfLanguageCode.Turkmen.TM
 | 
			
		||||
        IetfLanguageCode.Tagalog.code -> IetfLanguageCode.Tagalog
 | 
			
		||||
        IetfLanguageCode.Tswana.code -> IetfLanguageCode.Tswana
 | 
			
		||||
        IetfLanguageCode.TongaTongaIslands.code -> IetfLanguageCode.TongaTongaIslands
 | 
			
		||||
        IetfLanguageCode.TongaTongaIslands.TO.code -> IetfLanguageCode.TongaTongaIslands.TO
 | 
			
		||||
        IetfLanguageCode.Turkish.code -> IetfLanguageCode.Turkish
 | 
			
		||||
        IetfLanguageCode.Turkish.CY.code -> IetfLanguageCode.Turkish.CY
 | 
			
		||||
        IetfLanguageCode.Turkish.TR.code -> IetfLanguageCode.Turkish.TR
 | 
			
		||||
        IetfLanguageCode.Tsonga.code -> IetfLanguageCode.Tsonga
 | 
			
		||||
        IetfLanguageCode.Tatar.code -> IetfLanguageCode.Tatar
 | 
			
		||||
        IetfLanguageCode.Tatar.RU.code -> IetfLanguageCode.Tatar.RU
 | 
			
		||||
        IetfLanguageCode.Twi.code -> IetfLanguageCode.Twi
 | 
			
		||||
        IetfLanguageCode.Tahitian.code -> IetfLanguageCode.Tahitian
 | 
			
		||||
        IetfLanguageCode.UighurUyghur.code -> IetfLanguageCode.UighurUyghur
 | 
			
		||||
        IetfLanguageCode.UighurUyghur.CN.code -> IetfLanguageCode.UighurUyghur.CN
 | 
			
		||||
        IetfLanguageCode.Ukrainian.code -> IetfLanguageCode.Ukrainian
 | 
			
		||||
        IetfLanguageCode.Ukrainian.UA.code -> IetfLanguageCode.Ukrainian.UA
 | 
			
		||||
        IetfLanguageCode.Urdu.code -> IetfLanguageCode.Urdu
 | 
			
		||||
        IetfLanguageCode.Urdu.IN.code -> IetfLanguageCode.Urdu.IN
 | 
			
		||||
        IetfLanguageCode.Urdu.PK.code -> IetfLanguageCode.Urdu.PK
 | 
			
		||||
        IetfLanguageCode.Uzbek.code -> IetfLanguageCode.Uzbek
 | 
			
		||||
        IetfLanguageCode.Uzbek.Arab.code -> IetfLanguageCode.Uzbek.Arab
 | 
			
		||||
        IetfLanguageCode.Uzbek.Arab.AF.code -> IetfLanguageCode.Uzbek.Arab.AF
 | 
			
		||||
        IetfLanguageCode.Uzbek.Cyrl.code -> IetfLanguageCode.Uzbek.Cyrl
 | 
			
		||||
        IetfLanguageCode.Uzbek.Cyrl.UZ.code -> IetfLanguageCode.Uzbek.Cyrl.UZ
 | 
			
		||||
        IetfLanguageCode.Uzbek.Latn.code -> IetfLanguageCode.Uzbek.Latn
 | 
			
		||||
        IetfLanguageCode.Uzbek.Latn.UZ.code -> IetfLanguageCode.Uzbek.Latn.UZ
 | 
			
		||||
        IetfLanguageCode.Venda.code -> IetfLanguageCode.Venda
 | 
			
		||||
        IetfLanguageCode.Vietnamese.code -> IetfLanguageCode.Vietnamese
 | 
			
		||||
        IetfLanguageCode.Vietnamese.VN.code -> IetfLanguageCode.Vietnamese.VN
 | 
			
		||||
        IetfLanguageCode.Volapuk.code -> IetfLanguageCode.Volapuk
 | 
			
		||||
        IetfLanguageCode.Volapuk.L001.code -> IetfLanguageCode.Volapuk.L001
 | 
			
		||||
        IetfLanguageCode.Walloon.code -> IetfLanguageCode.Walloon
 | 
			
		||||
        IetfLanguageCode.Wolof.code -> IetfLanguageCode.Wolof
 | 
			
		||||
        IetfLanguageCode.Wolof.SN.code -> IetfLanguageCode.Wolof.SN
 | 
			
		||||
        IetfLanguageCode.Xhosa.code -> IetfLanguageCode.Xhosa
 | 
			
		||||
        IetfLanguageCode.Xhosa.ZA.code -> IetfLanguageCode.Xhosa.ZA
 | 
			
		||||
        IetfLanguageCode.Yiddish.code -> IetfLanguageCode.Yiddish
 | 
			
		||||
        IetfLanguageCode.Yiddish.L001.code -> IetfLanguageCode.Yiddish.L001
 | 
			
		||||
        IetfLanguageCode.Yoruba.code -> IetfLanguageCode.Yoruba
 | 
			
		||||
        IetfLanguageCode.Yoruba.BJ.code -> IetfLanguageCode.Yoruba.BJ
 | 
			
		||||
        IetfLanguageCode.Yoruba.NG.code -> IetfLanguageCode.Yoruba.NG
 | 
			
		||||
        IetfLanguageCode.ZhuangChuang.code -> IetfLanguageCode.ZhuangChuang
 | 
			
		||||
        IetfLanguageCode.Chinese.code -> IetfLanguageCode.Chinese
 | 
			
		||||
        IetfLanguageCode.Chinese.Hans.code -> IetfLanguageCode.Chinese.Hans
 | 
			
		||||
        IetfLanguageCode.Chinese.Hans.CN.code -> IetfLanguageCode.Chinese.Hans.CN
 | 
			
		||||
        IetfLanguageCode.Chinese.Hans.HK.code -> IetfLanguageCode.Chinese.Hans.HK
 | 
			
		||||
        IetfLanguageCode.Chinese.Hans.MO.code -> IetfLanguageCode.Chinese.Hans.MO
 | 
			
		||||
        IetfLanguageCode.Chinese.Hans.SG.code -> IetfLanguageCode.Chinese.Hans.SG
 | 
			
		||||
        IetfLanguageCode.Chinese.Hant.code -> IetfLanguageCode.Chinese.Hant
 | 
			
		||||
        IetfLanguageCode.Chinese.Hant.HK.code -> IetfLanguageCode.Chinese.Hant.HK
 | 
			
		||||
        IetfLanguageCode.Chinese.Hant.MO.code -> IetfLanguageCode.Chinese.Hant.MO
 | 
			
		||||
        IetfLanguageCode.Chinese.Hant.TW.code -> IetfLanguageCode.Chinese.Hant.TW
 | 
			
		||||
        IetfLanguageCode.Zulu.code -> IetfLanguageCode.Zulu
 | 
			
		||||
        IetfLanguageCode.Zulu.ZA.code -> IetfLanguageCode.Zulu.ZA
 | 
			
		||||
        else -> IetfLanguageCode.UnknownIetfLanguageCode(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
fun convertToIetfLanguageCode(code: String) = code.asIetfLanguageCode()
 | 
			
		||||
fun IetfLanguageCode(code: String) = code.asIetfLanguageCode()
 | 
			
		||||
							
								
								
									
										1
									
								
								language_codes/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								language_codes/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
<manifest package="dev.inmo.micro_utils.language_codes"/>
 | 
			
		||||
							
								
								
									
										52
									
								
								mime_types/mimes_generator/mime_generator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								mime_types/mimes_generator/mime_generator.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
import requests
 | 
			
		||||
from bs4 import BeautifulSoup
 | 
			
		||||
import pandas as pd
 | 
			
		||||
import itertools
 | 
			
		||||
 | 
			
		||||
def fix_name(category, raw_name):
 | 
			
		||||
    splitted = raw_name.replace('-', '+').replace('.', '+').replace(',', '+').split('+')
 | 
			
		||||
    out1 = ""
 | 
			
		||||
    for s in splitted:
 | 
			
		||||
        out1 += s.capitalize()
 | 
			
		||||
 | 
			
		||||
    result = ""
 | 
			
		||||
    if out1[0].isdigit():
 | 
			
		||||
        result += category[0].capitalize()
 | 
			
		||||
        result += out1
 | 
			
		||||
    else:
 | 
			
		||||
        result += out1
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    df = pd.read_html(open('table.html', 'r'))
 | 
			
		||||
    mimes = []
 | 
			
		||||
    for row in df[0].iterrows():
 | 
			
		||||
        mime = row[1][1]
 | 
			
		||||
        mime_category = mime.split('/', 1)[0]
 | 
			
		||||
        mime_name = mime.split('/', 1)[1]
 | 
			
		||||
        mimes.append({
 | 
			
		||||
            'mime_category': mime_category,
 | 
			
		||||
            'mime_name': mime_name,
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    # codegen
 | 
			
		||||
 | 
			
		||||
    mimes.sort(key=lambda x: x['mime_category'])
 | 
			
		||||
    grouped = itertools.groupby(mimes, lambda x: x['mime_category'])
 | 
			
		||||
    code = ''
 | 
			
		||||
    code2 = 'internal val knownMimeTypes: Set<MimeType> = setOf(\n'
 | 
			
		||||
    code2 += '    KnownMimeTypes.Any,\n'
 | 
			
		||||
    for key, group in grouped:
 | 
			
		||||
        group_name = key.capitalize()
 | 
			
		||||
        code += '@Serializable(MimeTypeSerializer::class)\nsealed class %s(raw: String) : MimeType, KnownMimeTypes(raw) {\n' % group_name
 | 
			
		||||
        code += '    @Serializable(MimeTypeSerializer::class)\n    object Any: %s ("%s/*")\n' % (group_name, key)
 | 
			
		||||
        for mime in group:
 | 
			
		||||
            name = fix_name(mime['mime_category'], mime['mime_name'])
 | 
			
		||||
            code += '    @Serializable(MimeTypeSerializer::class)\n    object %s: %s ("%s/%s")\n' % (name, group_name, mime['mime_category'], mime['mime_name'])
 | 
			
		||||
            code2 += '    KnownMimeTypes.%s.%s,\n' % (group_name, name)
 | 
			
		||||
        code += '}\n\n'
 | 
			
		||||
    code2 += ')\n'
 | 
			
		||||
    with open('out1.txt', 'w') as file:
 | 
			
		||||
        file.write(code)
 | 
			
		||||
    with open('out2.txt', 'w') as file:
 | 
			
		||||
        file.write(code2)
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
@file:Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
 | 
			
		||||
 | 
			
		||||
package dev.inmo.micro_utils.mime_types
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 
 | 
			
		||||
@@ -24,3 +24,8 @@ kotlin {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$defaultAndroidSettingsPresetPath"
 | 
			
		||||
 | 
			
		||||
java {
 | 
			
		||||
    sourceCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
    targetCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,11 @@ apply from: "$publishGradlePath"
 | 
			
		||||
 | 
			
		||||
kotlin {
 | 
			
		||||
    jvm {
 | 
			
		||||
        compilations.main.kotlinOptions.useIR = true
 | 
			
		||||
        compilations.main {
 | 
			
		||||
            kotlinOptions {
 | 
			
		||||
                jvmTarget = "1.8"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sourceSets {
 | 
			
		||||
@@ -28,3 +32,8 @@ kotlin {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
java {
 | 
			
		||||
    sourceCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
    targetCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,13 @@ apply from: "$publishGradlePath"
 | 
			
		||||
 | 
			
		||||
kotlin {
 | 
			
		||||
    jvm {
 | 
			
		||||
        compilations.main.kotlinOptions.useIR = true
 | 
			
		||||
        compilations.main {
 | 
			
		||||
            kotlinOptions {
 | 
			
		||||
                jvmTarget = "1.8"
 | 
			
		||||
            }
 | 
			
		||||
    js (BOTH) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    js (IR) {
 | 
			
		||||
        browser()
 | 
			
		||||
        nodejs()
 | 
			
		||||
    }
 | 
			
		||||
@@ -50,3 +54,8 @@ kotlin {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$defaultAndroidSettingsPresetPath"
 | 
			
		||||
 | 
			
		||||
java {
 | 
			
		||||
    sourceCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
    targetCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,3 +5,13 @@ plugins {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$mppProjectWithSerializationPresetPath"
 | 
			
		||||
 | 
			
		||||
kotlin {
 | 
			
		||||
    sourceSets {
 | 
			
		||||
        commonMain {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                api project(":micro_utils.common")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package dev.inmo.micro_utils.pagination
 | 
			
		||||
 | 
			
		||||
import dev.inmo.micro_utils.common.intersect
 | 
			
		||||
import kotlin.math.ceil
 | 
			
		||||
import kotlin.math.floor
 | 
			
		||||
 | 
			
		||||
@@ -9,7 +10,7 @@ import kotlin.math.floor
 | 
			
		||||
 * If you want to request something, you should use [SimplePagination]. If you need to return some result including
 | 
			
		||||
 * pagination - [PaginationResult]
 | 
			
		||||
 */
 | 
			
		||||
interface Pagination {
 | 
			
		||||
interface Pagination : ClosedRange<Int> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Started with 0.
 | 
			
		||||
     * Number of page inside of pagination. Offset can be calculated as [page] * [size]
 | 
			
		||||
@@ -20,6 +21,17 @@ interface Pagination {
 | 
			
		||||
     * Size of current page. Offset can be calculated as [page] * [size]
 | 
			
		||||
     */
 | 
			
		||||
    val size: Int
 | 
			
		||||
 | 
			
		||||
    override val start: Int
 | 
			
		||||
        get() = page * size
 | 
			
		||||
    override val endInclusive: Int
 | 
			
		||||
        get() = start + size - 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun Pagination.intersect(
 | 
			
		||||
    other: Pagination
 | 
			
		||||
): Pagination? = (this as ClosedRange<Int>).intersect(other as ClosedRange<Int>) ?.let {
 | 
			
		||||
    PaginationByIndexes(it.first, it.second)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -32,7 +44,7 @@ inline val Pagination.isFirstPage
 | 
			
		||||
 * First number in index of objects. It can be used as offset for databases or other data sources
 | 
			
		||||
 */
 | 
			
		||||
val Pagination.firstIndex: Int
 | 
			
		||||
    get() = page * size
 | 
			
		||||
    get() = start
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Last number in index of objects. In fact, one [Pagination] object represent data in next range:
 | 
			
		||||
@@ -41,7 +53,7 @@ val Pagination.firstIndex: Int
 | 
			
		||||
 * you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19. Here [Pagination.lastIndexExclusive] == 20
 | 
			
		||||
 */
 | 
			
		||||
val Pagination.lastIndexExclusive: Int
 | 
			
		||||
    get() = firstIndex + size
 | 
			
		||||
    get() = endInclusive + 1
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Last number in index of objects. In fact, one [Pagination] object represent data in next range:
 | 
			
		||||
@@ -50,7 +62,7 @@ val Pagination.lastIndexExclusive: Int
 | 
			
		||||
 * you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19.
 | 
			
		||||
 */
 | 
			
		||||
val Pagination.lastIndex: Int
 | 
			
		||||
    get() = lastIndexExclusive - 1
 | 
			
		||||
    get() = endInclusive
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculates pages count for given [datasetSize]
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,16 @@ suspend fun <T> getAll(
 | 
			
		||||
    return results.toList()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun <T, R> R.getAllBy(
 | 
			
		||||
    initialPagination: Pagination = FirstPagePagination(),
 | 
			
		||||
    paginationMapper: R.(PaginationResult<T>) -> Pagination?,
 | 
			
		||||
    block: suspend R.(Pagination) -> PaginationResult<T>
 | 
			
		||||
): List<T> = getAll(
 | 
			
		||||
    initialPagination,
 | 
			
		||||
    { paginationMapper(it) },
 | 
			
		||||
    { block(it) }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
suspend fun <T> getAllWithNextPaging(
 | 
			
		||||
    initialPagination: Pagination = FirstPagePagination(),
 | 
			
		||||
    block: suspend (Pagination) -> PaginationResult<T>
 | 
			
		||||
@@ -25,6 +35,14 @@ suspend fun <T> getAllWithNextPaging(
 | 
			
		||||
    block
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
suspend fun <T, R> R.getAllByWithNextPaging(
 | 
			
		||||
    initialPagination: Pagination = FirstPagePagination(),
 | 
			
		||||
    block: suspend R.(Pagination) -> PaginationResult<T>
 | 
			
		||||
): List<T> = getAllWithNextPaging(
 | 
			
		||||
    initialPagination,
 | 
			
		||||
    { block(it) }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
suspend fun <T> getAllWithCurrentPaging(
 | 
			
		||||
    initialPagination: Pagination = FirstPagePagination(),
 | 
			
		||||
    block: suspend (Pagination) -> PaginationResult<T>
 | 
			
		||||
@@ -33,3 +51,11 @@ suspend fun <T> getAllWithCurrentPaging(
 | 
			
		||||
    { it.currentPageIfNotEmpty() },
 | 
			
		||||
    block
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
suspend fun <T, R> R.getAllByWithCurrentPaging(
 | 
			
		||||
    initialPagination: Pagination = FirstPagePagination(),
 | 
			
		||||
    block: suspend R.(Pagination) -> PaginationResult<T>
 | 
			
		||||
): List<T> = getAllWithCurrentPaging(
 | 
			
		||||
    initialPagination,
 | 
			
		||||
    { block(it) }
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
apply plugin: 'maven-publish'
 | 
			
		||||
apply plugin: 'signing'
 | 
			
		||||
 | 
			
		||||
task javadocsJar(type: Jar) {
 | 
			
		||||
    classifier = 'javadoc'
 | 
			
		||||
@@ -70,7 +69,18 @@ publishing {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    
 | 
			
		||||
if (project.hasProperty("signing.gnupg.keyName")) {
 | 
			
		||||
    apply plugin: 'signing'
 | 
			
		||||
    
 | 
			
		||||
    signing {
 | 
			
		||||
        useGpgCmd()
 | 
			
		||||
    
 | 
			
		||||
        sign publishing.publications
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    task signAll {
 | 
			
		||||
        tasks.withType(Sign).forEach {
 | 
			
		||||
            dependsOn(it)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","includeGpgSigning":true,"developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}]}}
 | 
			
		||||
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}
 | 
			
		||||
@@ -2,6 +2,8 @@ package dev.inmo.micro_utils.repos
 | 
			
		||||
 | 
			
		||||
import android.database.Cursor
 | 
			
		||||
import android.database.sqlite.SQLiteDatabase
 | 
			
		||||
import androidx.core.database.*
 | 
			
		||||
import dev.inmo.micro_utils.repos.getLongOrNull
 | 
			
		||||
 | 
			
		||||
fun createTableQuery(
 | 
			
		||||
    tableName: String,
 | 
			
		||||
@@ -32,6 +34,11 @@ fun SQLiteDatabase.createTable(
 | 
			
		||||
fun Cursor.getString(columnName: String): String = getString(
 | 
			
		||||
    getColumnIndexOrThrow(columnName)
 | 
			
		||||
)
 | 
			
		||||
fun Cursor.getStringOrNull(columnName: String): String? {
 | 
			
		||||
    return getStringOrNull(
 | 
			
		||||
        getColumnIndex(columnName).takeIf { it != -1 } ?: return null
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @throws IllegalArgumentException
 | 
			
		||||
@@ -39,6 +46,11 @@ fun Cursor.getString(columnName: String): String = getString(
 | 
			
		||||
fun Cursor.getShort(columnName: String): Short = getShort(
 | 
			
		||||
    getColumnIndexOrThrow(columnName)
 | 
			
		||||
)
 | 
			
		||||
fun Cursor.getShortOrNull(columnName: String): Short? {
 | 
			
		||||
    return getShortOrNull(
 | 
			
		||||
        getColumnIndex(columnName).takeIf { it != -1 } ?: return null
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @throws IllegalArgumentException
 | 
			
		||||
@@ -46,6 +58,11 @@ fun Cursor.getShort(columnName: String): Short = getShort(
 | 
			
		||||
fun Cursor.getLong(columnName: String): Long = getLong(
 | 
			
		||||
    getColumnIndexOrThrow(columnName)
 | 
			
		||||
)
 | 
			
		||||
fun Cursor.getLongOrNull(columnName: String): Long? {
 | 
			
		||||
    return getLongOrNull(
 | 
			
		||||
        getColumnIndex(columnName).takeIf { it != -1 } ?: return null
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @throws IllegalArgumentException
 | 
			
		||||
@@ -53,6 +70,23 @@ fun Cursor.getLong(columnName: String): Long = getLong(
 | 
			
		||||
fun Cursor.getInt(columnName: String): Int = getInt(
 | 
			
		||||
    getColumnIndexOrThrow(columnName)
 | 
			
		||||
)
 | 
			
		||||
fun Cursor.getIntOrNull(columnName: String): Int? {
 | 
			
		||||
    return getIntOrNull(
 | 
			
		||||
        getColumnIndex(columnName).takeIf { it != -1 } ?: return null
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @throws IllegalArgumentException
 | 
			
		||||
 */
 | 
			
		||||
fun Cursor.getFloat(columnName: String): Float = getFloat(
 | 
			
		||||
    getColumnIndexOrThrow(columnName)
 | 
			
		||||
)
 | 
			
		||||
fun Cursor.getFloatOrNull(columnName: String): Float? {
 | 
			
		||||
    return getFloatOrNull(
 | 
			
		||||
        getColumnIndex(columnName).takeIf { it != -1 } ?: return null
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @throws IllegalArgumentException
 | 
			
		||||
@@ -60,6 +94,11 @@ fun Cursor.getInt(columnName: String): Int = getInt(
 | 
			
		||||
fun Cursor.getDouble(columnName: String): Double = getDouble(
 | 
			
		||||
    getColumnIndexOrThrow(columnName)
 | 
			
		||||
)
 | 
			
		||||
fun Cursor.getDoubleOrNull(columnName: String): Double? {
 | 
			
		||||
    return getDoubleOrNull(
 | 
			
		||||
        getColumnIndex(columnName).takeIf { it != -1 } ?: return null
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun SQLiteDatabase.select(
 | 
			
		||||
    table: String,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,15 @@ import dev.inmo.micro_utils.repos.*
 | 
			
		||||
import kotlinx.coroutines.flow.*
 | 
			
		||||
 | 
			
		||||
abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>(
 | 
			
		||||
    helper: StandardSQLHelper
 | 
			
		||||
    helper: StandardSQLHelper,
 | 
			
		||||
    replyInFlows: Int = 0,
 | 
			
		||||
    extraBufferCapacityInFlows: Int = 64
 | 
			
		||||
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>,
 | 
			
		||||
    AbstractAndroidCRUDRepo<ObjectType, IdType>(helper),
 | 
			
		||||
    StandardCRUDRepo<ObjectType, IdType, InputValueType> {
 | 
			
		||||
    protected val newObjectsChannel = MutableSharedFlow<ObjectType>(64)
 | 
			
		||||
    protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(64)
 | 
			
		||||
    protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(64)
 | 
			
		||||
    protected val newObjectsChannel = MutableSharedFlow<ObjectType>(replyInFlows, extraBufferCapacityInFlows)
 | 
			
		||||
    protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(replyInFlows, extraBufferCapacityInFlows)
 | 
			
		||||
    protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(replyInFlows, extraBufferCapacityInFlows)
 | 
			
		||||
    override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asSharedFlow()
 | 
			
		||||
    override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow()
 | 
			
		||||
    override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asSharedFlow()
 | 
			
		||||
 
 | 
			
		||||
@@ -10,15 +10,16 @@ import org.jetbrains.exposed.sql.transactions.transaction
 | 
			
		||||
 | 
			
		||||
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
 | 
			
		||||
    flowsChannelsSize: Int = 0,
 | 
			
		||||
    tableName: String = ""
 | 
			
		||||
    tableName: String = "",
 | 
			
		||||
    replyCacheInFlows: Int = 0
 | 
			
		||||
) :
 | 
			
		||||
    AbstractExposedReadCRUDRepo<ObjectType, IdType>(tableName),
 | 
			
		||||
    ExposedCRUDRepo<ObjectType, IdType>,
 | 
			
		||||
    WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
 | 
			
		||||
{
 | 
			
		||||
    protected val newObjectsChannel = MutableSharedFlow<ObjectType>(flowsChannelsSize)
 | 
			
		||||
    protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(flowsChannelsSize)
 | 
			
		||||
    protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(flowsChannelsSize)
 | 
			
		||||
    protected val newObjectsChannel = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
 | 
			
		||||
    protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
 | 
			
		||||
    protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(replyCacheInFlows, flowsChannelsSize)
 | 
			
		||||
 | 
			
		||||
    override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asSharedFlow()
 | 
			
		||||
    override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow()
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ open class ExposedKeyValueRepo<Key, Value>(
 | 
			
		||||
    valueColumnAllocator,
 | 
			
		||||
    tableName
 | 
			
		||||
) {
 | 
			
		||||
    private val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
 | 
			
		||||
    private val _onValueRemoved = MutableSharedFlow<Key>()
 | 
			
		||||
    protected val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
 | 
			
		||||
    protected val _onValueRemoved = MutableSharedFlow<Key>()
 | 
			
		||||
 | 
			
		||||
    override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
 | 
			
		||||
    override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,8 @@ open class ExposedReadKeyValueRepo<Key, Value>(
 | 
			
		||||
    valueColumnAllocator: ColumnAllocator<Value>,
 | 
			
		||||
    tableName: String? = null
 | 
			
		||||
) : ReadStandardKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
 | 
			
		||||
    protected val keyColumn: Column<Key> = keyColumnAllocator()
 | 
			
		||||
    protected val valueColumn: Column<Value> = valueColumnAllocator()
 | 
			
		||||
    val keyColumn: Column<Key> = keyColumnAllocator()
 | 
			
		||||
    val valueColumn: Column<Value> = valueColumnAllocator()
 | 
			
		||||
    override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
 | 
			
		||||
 | 
			
		||||
    init { initTable() }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.*
 | 
			
		||||
import org.jetbrains.exposed.sql.*
 | 
			
		||||
import org.jetbrains.exposed.sql.transactions.transaction
 | 
			
		||||
 | 
			
		||||
typealias ExposedKeyValuesRepo<Key, Value> = ExposedOneToManyKeyValueRepo<Key, Value>
 | 
			
		||||
open class ExposedOneToManyKeyValueRepo<Key, Value>(
 | 
			
		||||
    database: Database,
 | 
			
		||||
    keyColumnAllocator: ColumnAllocator<Key>,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,17 +3,20 @@ package dev.inmo.micro_utils.repos.exposed.onetomany
 | 
			
		||||
import dev.inmo.micro_utils.pagination.*
 | 
			
		||||
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
 | 
			
		||||
import dev.inmo.micro_utils.repos.exposed.*
 | 
			
		||||
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedReadKeyValueRepo
 | 
			
		||||
import org.jetbrains.exposed.sql.*
 | 
			
		||||
import org.jetbrains.exposed.sql.transactions.transaction
 | 
			
		||||
 | 
			
		||||
typealias ExposedReadKeyValuesRepo<Key, Value> = ExposedReadOneToManyKeyValueRepo<Key, Value>
 | 
			
		||||
 | 
			
		||||
open class ExposedReadOneToManyKeyValueRepo<Key, Value>(
 | 
			
		||||
    override val database: Database,
 | 
			
		||||
    keyColumnAllocator: ColumnAllocator<Key>,
 | 
			
		||||
    valueColumnAllocator: ColumnAllocator<Value>,
 | 
			
		||||
    tableName: String? = null
 | 
			
		||||
) : ReadOneToManyKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
 | 
			
		||||
    protected val keyColumn: Column<Key> = keyColumnAllocator()
 | 
			
		||||
    protected val valueColumn: Column<Value> = valueColumnAllocator()
 | 
			
		||||
    val keyColumn: Column<Key> = keyColumnAllocator()
 | 
			
		||||
    val valueColumn: Column<Value> = valueColumnAllocator()
 | 
			
		||||
 | 
			
		||||
    init { initTable() }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,8 @@ inline fun versionsRepo(database: Database): VersionsRepo<Database> = StandardVe
 | 
			
		||||
class ExposedStandardVersionsRepoProxy(
 | 
			
		||||
    override val database: Database
 | 
			
		||||
) : StandardVersionsRepoProxy<Database>, Table("ExposedVersionsProxy"), ExposedRepo {
 | 
			
		||||
    private val tableNameColumn = text("tableName")
 | 
			
		||||
    private val tableVersionColumn = integer("tableName")
 | 
			
		||||
    val tableNameColumn = text("tableName")
 | 
			
		||||
    val tableVersionColumn = integer("tableName")
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        initTable()
 | 
			
		||||
 
 | 
			
		||||
@@ -23,13 +23,13 @@ class ReadMapCRUDRepo<ObjectType, IdType>(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(
 | 
			
		||||
    private val map: MutableMap<IdType, ObjectType> = mutableMapOf()
 | 
			
		||||
    protected val map: MutableMap<IdType, ObjectType> = mutableMapOf()
 | 
			
		||||
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> {
 | 
			
		||||
    private val _newObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow()
 | 
			
		||||
    protected val _newObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow()
 | 
			
		||||
    override val newObjectsFlow: Flow<ObjectType> = _newObjectsFlow.asSharedFlow()
 | 
			
		||||
    private val _updatedObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow()
 | 
			
		||||
    protected val _updatedObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow()
 | 
			
		||||
    override val updatedObjectsFlow: Flow<ObjectType> = _updatedObjectsFlow.asSharedFlow()
 | 
			
		||||
    private val _deletedObjectsIdsFlow: MutableSharedFlow<IdType> = MutableSharedFlow()
 | 
			
		||||
    protected val _deletedObjectsIdsFlow: MutableSharedFlow<IdType> = MutableSharedFlow()
 | 
			
		||||
    override val deletedObjectsIdsFlow: Flow<IdType> = _deletedObjectsIdsFlow.asSharedFlow()
 | 
			
		||||
 | 
			
		||||
    protected abstract suspend fun updateObject(newValue: InputValueType, id: IdType, old: ObjectType): ObjectType
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.MutableSharedFlow
 | 
			
		||||
 | 
			
		||||
class ReadMapKeyValueRepo<Key, Value>(
 | 
			
		||||
    private val map: Map<Key, Value> = emptyMap()
 | 
			
		||||
    protected val map: Map<Key, Value> = emptyMap()
 | 
			
		||||
) : ReadStandardKeyValueRepo<Key, Value> {
 | 
			
		||||
    override suspend fun get(k: Key): Value? = map[k]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,20 +8,20 @@ import kotlin.reflect.KClass
 | 
			
		||||
 | 
			
		||||
open class TypedSerializer<T : Any>(
 | 
			
		||||
    kClass: KClass<T>,
 | 
			
		||||
    presetSerializers: Map<String, KSerializer<out T>> = emptyMap()
 | 
			
		||||
    presetSerializers: Map<String, KSerializer<out T>> = emptyMap(),
 | 
			
		||||
) : KSerializer<T> {
 | 
			
		||||
    protected val serializers = presetSerializers.toMutableMap()
 | 
			
		||||
    @InternalSerializationApi
 | 
			
		||||
    open override val descriptor: SerialDescriptor = buildSerialDescriptor(
 | 
			
		||||
        "TextSourceSerializer",
 | 
			
		||||
    @OptIn(InternalSerializationApi::class)
 | 
			
		||||
    override val descriptor: SerialDescriptor = buildSerialDescriptor(
 | 
			
		||||
        "TypedSerializer",
 | 
			
		||||
        SerialKind.CONTEXTUAL
 | 
			
		||||
    ) {
 | 
			
		||||
        element("type", String.serializer().descriptor)
 | 
			
		||||
        element("value", ContextualSerializer(kClass).descriptor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @InternalSerializationApi
 | 
			
		||||
    open override fun deserialize(decoder: Decoder): T {
 | 
			
		||||
    @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
 | 
			
		||||
    override fun deserialize(decoder: Decoder): T {
 | 
			
		||||
        return decoder.decodeStructure(descriptor) {
 | 
			
		||||
            var type: String? = null
 | 
			
		||||
            lateinit var result: T
 | 
			
		||||
@@ -44,13 +44,13 @@ open class TypedSerializer<T : Any>(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @InternalSerializationApi
 | 
			
		||||
    @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
 | 
			
		||||
    protected open fun <O: T> CompositeEncoder.encode(value: O) {
 | 
			
		||||
        encodeSerializableElement(descriptor, 1, value::class.serializer() as KSerializer<O>, value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @InternalSerializationApi
 | 
			
		||||
    open override fun serialize(encoder: Encoder, value: T) {
 | 
			
		||||
    @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
 | 
			
		||||
    override fun serialize(encoder: Encoder, value: T) {
 | 
			
		||||
        encoder.encodeStructure(descriptor) {
 | 
			
		||||
            val valueSerializer = value::class.serializer()
 | 
			
		||||
            val type = serializers.keys.first { serializers[it] == valueSerializer }
 | 
			
		||||
@@ -69,6 +69,20 @@ open class TypedSerializer<T : Any>(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@InternalSerializationApi
 | 
			
		||||
operator fun <T : Any> TypedSerializer<T>.plusAssign(kClass: KClass<T>) {
 | 
			
		||||
    include(kClass.simpleName!!, kClass.serializer())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@InternalSerializationApi
 | 
			
		||||
operator fun <T : Any> TypedSerializer<T>.minusAssign(kClass: KClass<T>) {
 | 
			
		||||
    exclude(kClass.simpleName!!)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <reified T : Any> TypedSerializer(
 | 
			
		||||
    presetSerializers: Map<String, KSerializer<out T>> = emptyMap()
 | 
			
		||||
) = TypedSerializer(T::class, presetSerializers)
 | 
			
		||||
 | 
			
		||||
inline fun <reified T : Any> TypedSerializer(
 | 
			
		||||
    vararg presetSerializers: Pair<String, KSerializer<out T>>
 | 
			
		||||
) = TypedSerializer(presetSerializers.toMap())
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ String[] includes = [
 | 
			
		||||
    ":pagination:ktor:common",
 | 
			
		||||
    ":pagination:ktor:server",
 | 
			
		||||
    ":mime_types",
 | 
			
		||||
    ":language_codes",
 | 
			
		||||
    ":language_codes:generator",
 | 
			
		||||
    ":repos:common",
 | 
			
		||||
    ":repos:cache",
 | 
			
		||||
    ":repos:exposed",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user