Compare commits

...

176 Commits

Author SHA1 Message Date
1c0b8cf842 Update CHANGELOG.md 2022-01-13 10:20:35 +06:00
a1624ea2a9 Update gradle.properties 2022-01-13 10:19:08 +06:00
23a050cf1e Merge pull request #122 from InsanusMokrassar/0.9.1
0.9.1
2022-01-08 16:12:04 +06:00
916f2f96f4 typealiases for exposed one to many 2022-01-08 14:35:28 +06:00
00cc214754 repo exposed updates 2022-01-08 14:14:44 +06:00
b2e38f72b9 start 0.9.1 2022-01-08 14:12:57 +06:00
e7107d238d Update dokka_push.yml 2021-12-30 02:55:19 +06:00
ed9ebdbd1a Merge pull request #121 from InsanusMokrassar/0.9.0
0.9.0
2021-12-30 02:52:34 +06:00
e80676d3d2 Update packages_push.yml 2021-12-29 19:43:19 +06:00
02d02fa8f2 update android tools build 2021-12-29 19:24:25 +06:00
bd783fb74f update dokka 2021-12-29 17:44:45 +06:00
50386adf70 ignore kotlin-js-store folder 2021-12-28 21:19:47 +06:00
f4ee6c2890 update exposed and adapt to new version of kotlin serialization 2021-12-28 21:17:17 +06:00
d45aef9fe5 start 0.9.0 2021-12-28 09:58:59 +06:00
a56cd3dddd Merge pull request #120 from InsanusMokrassar/0.8.9
0.8.9
2021-12-27 17:02:16 +06:00
419e7070ee more fixes to god of fixes 2021-12-27 17:02:00 +06:00
612cf40b5f small hotfix 2021-12-27 16:00:07 +06:00
8b39882e83 fixes in DefaultUpdatableStatesMachine 2021-12-27 15:57:55 +06:00
e639ae172b Fixes in uniloadMultipart 2021-12-27 15:55:05 +06:00
d0446850ae start 0.8.9 2021-12-27 15:45:44 +06:00
c48465b90b Merge pull request #119 from InsanusMokrassar/0.8.8
0.8.8
2021-12-26 22:11:04 +06:00
f419fd03d2 small fix of performing state update for UpdatableStatesMachine 2021-12-26 22:00:26 +06:00
494812a660 one more fix 2021-12-26 21:25:12 +06:00
eb78f21eec fix FSMBuilder 2021-12-26 21:21:40 +06:00
4bda70268b small fix of style in DefaultUpdatableStatesMachine 2021-12-26 21:07:40 +06:00
f037ce4371 updates in FSM 2021-12-26 21:06:26 +06:00
3d2196e35d update dependencies (which possible) 2021-12-26 20:07:13 +06:00
a74f061b02 start 0.8.8 2021-12-26 20:02:18 +06:00
11ade14676 Merge pull request #118 from InsanusMokrassar/0.8.7
0.8.7
2021-12-15 01:22:42 +06:00
eb562d8784 add stream using for multipart 2021-12-14 22:03:29 +06:00
1ee5b4bfd4 hotfix for multipart 2021-12-14 21:13:11 +06:00
d97892080b add preview work with multipart 2021-12-14 18:01:41 +06:00
6f37125724 start 0.8.7 and make UnifiedRequester/UnifiedRouter fields are public 2021-12-14 13:25:58 +06:00
ed1baaade7 Merge pull request #117 from InsanusMokrassar/0.8.6
0.8.6
2021-12-08 15:57:43 +06:00
bb9669f8fd fill changelog 2021-12-08 11:45:01 +06:00
bdac715d48 remove crossinline where possible 2021-12-08 11:41:49 +06:00
acf4971298 downgrade ktor 2021-12-08 11:36:24 +06:00
249bc83a8c update ktor 2021-11-27 16:22:13 +06:00
0fbb92f03f start 0.8.6 2021-11-27 16:20:56 +06:00
ca27cb3f82 Merge pull request #116 from InsanusMokrassar/0.8.5
0.8.5
2021-11-27 01:00:18 +06:00
3a5771a0cc Update RepeatOnFailure.kt 2021-11-26 23:07:00 +06:00
527a2a91ac repeatOnFailure 2021-11-26 19:45:59 +06:00
6763e5c4c6 start 0.8.5 2021-11-26 19:32:52 +06:00
06918d8310 add mime types generator python script 2021-11-24 14:31:24 +06:00
89ccaa1b57 Merge pull request #115 from InsanusMokrassar/0.8.4
0.8.4
2021-11-19 14:02:38 +06:00
5d0bdb9bcf fix pagination 2021-11-19 13:58:59 +06:00
31fdcf74a5 ktor server createKtorServer extensions 2021-11-19 13:24:45 +06:00
afca09cc1d start 0.8.4 2021-11-19 13:18:37 +06:00
531d89d9db Merge pull request #114 from InsanusMokrassar/0.8.3
0.8.3
2021-11-19 09:49:35 +06:00
6bbbea0bc3 suppressions in Optional.kt 2021-11-18 23:03:24 +06:00
e337cd98c8 optional workaround 2021-11-17 21:31:35 +06:00
bcbab3b380 add Optional type 2021-11-17 17:38:41 +06:00
fb63de7568 intersect 2021-11-17 14:22:45 +06:00
aa45a4ab13 now Pagination is an ClosedRange 2021-11-17 14:07:11 +06:00
2af7e2f681 start 0.8.3 2021-11-17 14:01:44 +06:00
34fd9edce0 Merge pull request #113 from InsanusMokrassar/0.8.2
0.8.2
2021-11-12 13:23:39 +06:00
2a4cb8c5f9 improvements in FSM 2021-11-12 13:19:15 +06:00
50ea40bc3a add changelog for dependencies update 2021-11-12 13:14:57 +06:00
a77654052d Update gradle.properties 2021-11-11 21:20:17 +06:00
88aafce552 start 0.8.2 2021-11-11 20:31:04 +06:00
4e95d6bfff Merge pull request #112 from InsanusMokrassar/0.8.1
0.8.1
2021-11-09 14:04:11 +06:00
38d0e34fb5 updates in scripts and update core ktx 2021-11-09 14:03:51 +06:00
8fbc6b9041 update changelog 2021-11-09 13:43:46 +06:00
e8219d6cf4 start 0.8.1 2021-11-09 10:21:23 +06:00
6c20fc4ca6 Merge pull request #111 from InsanusMokrassar/0.8.0
0.8.0
2021-11-05 21:56:00 +06:00
85cd975492 preparations for release 2021-11-05 21:45:01 +06:00
1171a717fe optimize imports 2021-11-05 15:49:39 +06:00
bbe5320312 rework of FSM + 0.8.0 2021-11-05 15:43:05 +06:00
00acb9fddd add suppresses to the generated classes 2021-11-03 17:22:32 +06:00
de3d14dc41 solution of #109 2021-11-03 15:36:25 +06:00
67ff9cc9b3 update dependencies 2021-11-03 15:18:41 +06:00
af132103a0 start 0.7.5 and add either serializer 2021-11-02 12:43:59 +06:00
3b1124a804 fixes 2021-10-30 12:17:18 +06:00
f226c2dfd6 Merge pull request #106 from InsanusMokrassar/0.7.4
0.7.4
2021-10-29 13:57:58 +06:00
69d6e63846 getAllBy* 2021-10-29 13:51:59 +06:00
02c3d397ad solution of #104 2021-10-29 13:40:53 +06:00
67a1050646 solution for #105 2021-10-28 18:46:53 +06:00
8cd0775a6c update kdocs of Either 2021-10-28 18:42:05 +06:00
162294d6c6 either 2021-10-28 18:02:02 +06:00
c4dd19dd00 start 0.7.4 2021-10-28 17:50:28 +06:00
d2314422f1 Merge pull request #103 from InsanusMokrassar/0.7.3
0.7.3
2021-10-23 14:26:42 +06:00
6fedd6f859 update dependencies 2021-10-23 14:24:31 +06:00
e52b59665f start 0.7.3 2021-10-23 14:19:30 +06:00
cda9d09689 fixes for kdocs 2021-10-18 22:57:25 +06:00
c9237b3f00 Merge pull request #102 from InsanusMokrassar/0.7.2
0.7.2
2021-10-16 09:51:24 +06:00
18bba66c4a Update CHANGELOG.md 2021-10-16 08:56:38 +06:00
63418c4a8a Update gradle.properties 2021-10-16 08:55:44 +06:00
2e66c6f4e3 Merge pull request #101 from InsanusMokrassar/0.7.1
0.7.1
2021-10-13 15:11:26 +06:00
e9c5df4c13 upfill kdocs 2021-10-13 15:09:05 +06:00
bc7789ad2c add kdocs 2021-10-13 15:06:58 +06:00
e3da761249 Fill changelog 2021-10-13 14:38:50 +06:00
4082f65afa AccumulatorFlow 2021-10-13 13:26:39 +06:00
5d1cab075d Update gradle.properties 2021-10-12 21:03:47 +06:00
bcf67f7e59 Update gradle.properties 2021-10-12 20:56:00 +06:00
7d3b1f8e75 StatesMachine is interface 2021-10-06 13:56:58 +06:00
119a0588cc DefaultStatesManager 2021-10-06 13:30:25 +06:00
fab789d9c0 start rework of FSM states manager 2021-10-06 12:14:02 +06:00
ceba81c08f start 0.7.1 2021-10-06 11:51:55 +06:00
a061af0558 actualize changelog 2021-10-05 13:52:28 +06:00
c7a53846ad Merge pull request #100 from InsanusMokrassar/0.7.0
0.7.0 + migration back to klock
2021-10-05 13:51:41 +06:00
a683cccf0c 0.7.0 + migration back to klock 2021-10-05 13:46:23 +06:00
50d41e35c1 remove deprecations 2021-10-04 16:09:01 +06:00
aa0e831cea Merge pull request #99 from InsanusMokrassar/0.6.0
0.6.0
2021-10-04 15:57:02 +06:00
44e26ccb4f migration onto datetime 2021-10-04 15:54:43 +06:00
2a783f6e2b start 0.6.0 2021-10-03 18:58:15 +06:00
6058d6a724 update ktor 2021-10-01 16:12:26 +06:00
2e9c7eb5fa Merge pull request #98 from InsanusMokrassar/0.5.31
0.5.31
2021-10-01 15:42:24 +06:00
e75465ad10 update dependencies 2021-09-30 11:59:44 +06:00
de01ad54e9 start 0.5.31 2021-09-30 11:57:29 +06:00
eeea7ddbe3 Merge pull request #97 from InsanusMokrassar/0.5.30
0.5.30
2021-09-25 16:22:34 +06:00
e0b18bec05 update dependencies 2021-09-25 14:55:56 +06:00
410e89bba9 start 0.5.30 2021-09-25 14:45:56 +06:00
9ef19dc42b Merge pull request #96 from InsanusMokrassar/0.5.29
0.5.29
2021-09-23 13:45:55 +06:00
0337d1b82d add fix of 31.0.0 for kdocs workflow 2021-09-23 13:23:02 +06:00
f5bd4c5ccb update dependencies 2021-09-23 13:12:03 +06:00
630f9bc0d4 start 0.5.29 2021-09-23 13:10:44 +06:00
18b4ffece1 Update packages_push.yml 2021-09-22 20:18:14 +06:00
f64e1effa3 Delete build.yml 2021-09-22 20:17:04 +06:00
847fcbb488 Merge pull request #95 from InsanusMokrassar/0.5.28
0.5.28
2021-09-19 21:59:25 +06:00
88002ec8e7 set java toolchain version in all projects related to Java 2021-09-19 20:50:24 +06:00
7f8db6a29d update dependencies 2021-09-19 20:39:15 +06:00
b183b82443 start 0.5.28 2021-09-19 20:36:02 +06:00
5dad27de72 Merge pull request #94 from InsanusMokrassar/0.5.27
0.5.27
2021-09-15 21:12:34 +06:00
6b66084d0e update dependencies 2021-09-15 19:07:36 +06:00
50b56a7c39 start 0.5.27 2021-09-15 19:04:02 +06:00
7ab7d14471 Merge pull request #93 from InsanusMokrassar/0.5.26
0.5.26
2021-09-09 12:25:12 +06:00
bdcc179b7b protecteds in map repos instead of privates 2021-09-09 12:16:12 +06:00
55ffd4b46f start 0.5.26 2021-09-09 12:07:56 +06:00
7fc5ee70e1 Merge pull request #92 from InsanusMokrassar/0.5.25
0.5.25
2021-09-08 12:27:31 +06:00
a24a335743 TypedSerializer#plusAssign and TypedSerializer#minusAssign 2021-09-08 12:18:42 +06:00
ef9af71960 clamp deprecation and Iterable#diff 2021-09-08 12:10:32 +06:00
925702d315 MPPFile#withoutSlashAtTheEnd 2021-09-08 12:06:23 +06:00
d50dffec8c update dependencies 2021-09-08 12:01:02 +06:00
cef2081a13 start 0.5.25 2021-09-08 11:57:55 +06:00
06c8bde7c9 Merge pull request #91 from InsanusMokrassar/0.5.24
0.5.24
2021-09-04 14:59:13 +06:00
c9bbfa3820 update gradle config and fix build 2021-09-04 14:58:15 +06:00
eed7cfdc42 CoroutineScope with safely handler parameter 2021-09-04 14:46:12 +06:00
bd9b0d16ab update dependencies 2021-09-04 14:34:27 +06:00
ea6c33b497 start 0.5.24 2021-09-04 14:20:35 +06:00
dc80ade2fb Merge pull request #90 from InsanusMokrassar/0.5.23
0.5.23
2021-09-02 14:35:31 +06:00
f6a06ee8ea small addition to joinTo 2021-09-02 14:33:35 +06:00
2644f27975 small addition to joinTo 2021-09-02 14:16:02 +06:00
3dc68a7b8b small addition to joinTo 2021-09-02 14:09:34 +06:00
97fc1d6239 small addition to joinTo 2021-09-02 14:01:01 +06:00
662f4d22a3 fill changelog 2021-09-02 13:52:00 +06:00
b70aa12be9 add joinTo 2021-09-02 13:50:40 +06:00
71f12f5f19 Update exposed 2021-09-02 10:09:45 +06:00
e10504eeeb Update CHANGELOG.md 2021-09-02 10:09:06 +06:00
2dea9f3bc0 start 0.5.23 2021-09-02 10:08:28 +06:00
35c9dda5bc Merge pull request #89 from InsanusMokrassar/0.5.22
0.5.22 - Update ktor
2021-08-27 08:57:57 +06:00
e831f3949a Update CHANGELOG.md 2021-08-26 14:30:37 +06:00
b0b39cc693 Update gradle.properties 2021-08-26 14:28:21 +06:00
fc03be3f73 Merge pull request #88 from InsanusMokrassar/0.5.22
0.5.22
2021-08-25 16:35:02 +06:00
b61f6b81f1 update dependencies 2021-08-25 15:50:10 +06:00
f5bc1c1fce start 0.5.22 2021-08-25 15:10:55 +06:00
a729f9568c Merge pull request #87 from InsanusMokrassar/0.5.21
0.5.21
2021-08-17 11:01:03 +06:00
5749e00377 update klock 2021-08-17 10:36:15 +06:00
ef73c24a0c fixes in TypedSerializer 2021-08-17 10:35:45 +06:00
94717ee351 start 0.5.21 2021-08-17 10:30:50 +06:00
9a18ded65b Merge pull request #86 from InsanusMokrassar/0.5.20
0.5.20
2021-08-16 19:14:56 +06:00
b23220f491 cursor get float and cursor get*OrNull 2021-08-16 18:42:08 +06:00
6e6bb03246 start 0.5.20 2021-08-16 18:35:46 +06:00
1ae6bae3b8 Merge pull request #85 from InsanusMokrassar/0.5.19
0.5.19#2
2021-08-09 11:24:06 +06:00
1239ca3256 update exposed 2021-08-09 11:23:22 +06:00
57b7797ea4 Merge pull request #84 from InsanusMokrassar/0.5.19
0.5.19
2021-08-08 22:52:09 +06:00
5ee5bfd1d5 updates in IetfLanguageCode 2021-08-08 22:09:33 +06:00
7229a3e198 start 0.5.19 2021-08-08 21:44:06 +06:00
bee083582f Merge pull request #83 from InsanusMokrassar/0.5.18
0.5.18
2021-08-04 11:53:40 +06:00
9d7f99f286 add several functions for string to language code conversation 2021-08-04 11:51:02 +06:00
6ef403853c Merge pull request #82 from InsanusMokrassar/0.5.18
0.5.18
2021-08-04 11:26:06 +06:00
6ae7ccb9a1 add kdocs to language_codes 2021-08-04 11:14:56 +06:00
dafc50c463 small reformat of code for language_codes 2021-08-04 11:11:43 +06:00
e89e2c931d real creating of module language code -.- 2021-08-04 11:04:03 +06:00
43a67b99e4 add language_codes 2021-08-04 11:03:25 +06:00
46c48f4f31 start 0.5.18 2021-08-04 11:03:01 +06:00
bf0fe85aa6 Merge pull request #81 from InsanusMokrassar/0.5.17
0.5.17
2021-07-31 16:36:17 +06:00
75 changed files with 5425 additions and 347 deletions

View File

@@ -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

View File

@@ -10,7 +10,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-java@v1 - uses: actions/setup-java@v1
with: 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 - name: Build
run: ./gradlew dokkaHtml run: ./gradlew dokkaHtml
- name: Publish KDocs - name: Publish KDocs

View File

@@ -8,7 +8,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-java@v1 - uses: actions/setup-java@v1
with: 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 - name: Rewrite version
run: | run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`" branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
@@ -18,6 +21,7 @@ jobs:
- name: Build - name: Build
run: ./gradlew build run: ./gradlew build
- name: Publish - name: Publish
continue-on-error: true
run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signAndroidDebugPublication -x signAndroidReleasePublication -x signKotlinMultiplatformPublication run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signAndroidDebugPublication -x signAndroidReleasePublication -x signKotlinMultiplatformPublication
env: env:
GITHUBPACKAGES_USER: ${{ github.actor }} GITHUBPACKAGES_USER: ${{ github.actor }}

1
.gitignore vendored
View File

@@ -11,5 +11,6 @@ out/
secret.gradle secret.gradle
local.properties local.properties
kotlin-js-store
publishing.sh publishing.sh

View File

@@ -1,5 +1,273 @@
# Changelog # Changelog
## 0.9.2
* `Versions`:
* `Klock`: `2.4.10` -> `2.4.12`
## 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 ## 0.5.17
**SINCE THIS UPDATE JS PARTS WILL BE COMPILED WITH IR COMPILER ONLY** **SINCE THIS UPDATE JS PARTS WILL BE COMPILED WITH IR COMPILER ONLY**

View File

@@ -7,7 +7,7 @@ buildscript {
} }
dependencies { 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-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version" classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"

View File

@@ -12,9 +12,7 @@ package dev.inmo.micro_utils.common
AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.FUNCTION, AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE, AnnotationTarget.TYPEALIAS
AnnotationTarget.TYPEALIAS,
AnnotationTarget.TYPE_PARAMETER
) )
annotation class PreviewFeature(val message: String = "It is possible, that behaviour of this thing will be changed or removed in future releases") 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_GETTER,
AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.FUNCTION, AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE, AnnotationTarget.TYPEALIAS
AnnotationTarget.TYPEALIAS,
AnnotationTarget.TYPE_PARAMETER
) )
annotation class Warning(val message: String) annotation class Warning(val message: String)

View File

@@ -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
}
}

View File

@@ -27,8 +27,8 @@ data class Diff<T> internal constructor(
private inline fun <T> performChanges( private inline fun <T> performChanges(
potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>, potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
additionalsInOld: MutableList<T>, additionsInOld: MutableList<T>,
additionalsInNew: MutableList<T>, additionsInNew: MutableList<T>,
changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>, changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
removedList: MutableList<IndexedValue<T>>, removedList: MutableList<IndexedValue<T>>,
addedList: MutableList<IndexedValue<T>>, addedList: MutableList<IndexedValue<T>>,
@@ -52,20 +52,20 @@ private inline fun <T> performChanges(
newPotentials.first().second ?.let { addedList.add(it) } newPotentials.first().second ?.let { addedList.add(it) }
newPotentials.drop(1).take(newPotentials.size - 2).forEach { (oldOne, newOne) -> newPotentials.drop(1).take(newPotentials.size - 2).forEach { (oldOne, newOne) ->
addedList.add(newOne!!) addedList.add(newOne!!)
oldOne ?.let { additionalsInOld.add(oldOne.value) } oldOne ?.let { additionsInOld.add(oldOne.value) }
} }
if (newPotentials.size > 1) { if (newPotentials.size > 1) {
newPotentials.last().first ?.value ?.let { additionalsInOld.add(it) } newPotentials.last().first ?.value ?.let { additionsInOld.add(it) }
} }
} }
newOneEqualToOldObject -> { newOneEqualToOldObject -> {
newPotentials.first().first ?.let { removedList.add(it) } newPotentials.first().first ?.let { removedList.add(it) }
newPotentials.drop(1).take(newPotentials.size - 2).forEach { (oldOne, newOne) -> newPotentials.drop(1).take(newPotentials.size - 2).forEach { (oldOne, newOne) ->
removedList.add(oldOne!!) removedList.add(oldOne!!)
newOne ?.let { additionalsInNew.add(newOne.value) } newOne ?.let { additionsInNew.add(newOne.value) }
} }
if (newPotentials.size > 1) { 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()) 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> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new)
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true) inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)

View File

@@ -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")
}

View File

@@ -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()

View File

@@ -7,7 +7,7 @@ import kotlin.jvm.JvmInline
@JvmInline @JvmInline
value class FileName(val string: String) { value class FileName(val string: String) {
val name: String val name: String
get() = string.takeLastWhile { it != '/' } get() = withoutSlashAtTheEnd.takeLastWhile { it != '/' }
val extension: String val extension: String
get() = name.takeLastWhile { it != '.' } get() = name.takeLastWhile { it != '.' }
val nameWithoutExtension: String val nameWithoutExtension: String
@@ -17,15 +17,18 @@ value class FileName(val string: String) {
filename.substring(0, it) filename.substring(0, it)
} ?: filename } ?: filename
} }
val withoutSlashAtTheEnd: String
get() = string.dropLastWhile { it == '/' }
override fun toString(): String = string override fun toString(): String = string
} }
@PreviewFeature
expect class MPPFile expect class MPPFile
expect val MPPFile.filename: FileName expect val MPPFile.filename: FileName
expect val MPPFile.filesize: Long expect val MPPFile.filesize: Long
expect val MPPFile.bytesAllocatorSync: ByteArrayAllocator
expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator
fun MPPFile.bytesSync() = bytesAllocatorSync()
suspend fun MPPFile.bytes() = bytesAllocator() suspend fun MPPFile.bytes() = bytesAllocator()

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -11,7 +11,7 @@ class DiffUtilsTests {
val withIndex = oldList.withIndex() val withIndex = oldList.withIndex()
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) { for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
for ((i, v) in withIndex) { for ((i, _) in withIndex) {
if (i + count > oldList.lastIndex) { if (i + count > oldList.lastIndex) {
continue continue
} }

View File

@@ -2,10 +2,12 @@ package dev.inmo.micro_utils.common
import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.ArrayBuffer
import org.w3c.dom.ErrorEvent import org.w3c.dom.ErrorEvent
import org.w3c.files.File import org.w3c.files.*
import org.w3c.files.FileReader
import kotlin.js.Promise import kotlin.js.Promise
/**
* @suppress
*/
actual typealias MPPFile = File actual typealias MPPFile = File
fun MPPFile.readBytesPromise() = Promise<ByteArray> { success, failure -> fun MPPFile.readBytesPromise() = Promise<ByteArray> { success, failure ->
@@ -21,12 +23,32 @@ fun MPPFile.readBytesPromise() = Promise<ByteArray> { success, failure ->
reader.readAsArrayBuffer(this) reader.readAsArrayBuffer(this)
} }
fun MPPFile.readBytes(): ByteArray {
val reader = FileReaderSync()
return reader.readAsArrayBuffer(this).toByteArray()
}
private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().await() private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().await()
/**
* @suppress
*/
actual val MPPFile.filename: FileName actual val MPPFile.filename: FileName
get() = FileName(name) get() = FileName(name)
/**
* @suppress
*/
actual val MPPFile.filesize: Long actual val MPPFile.filesize: Long
get() = size.toLong() 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") @Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can")
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = ::dirtyReadBytes get() = ::dirtyReadBytes

View File

@@ -4,12 +4,29 @@ import dev.inmo.micro_utils.coroutines.doInIO
import dev.inmo.micro_utils.coroutines.doOutsideOfCoroutine import dev.inmo.micro_utils.coroutines.doOutsideOfCoroutine
import java.io.File import java.io.File
/**
* @suppress
*/
actual typealias MPPFile = File actual typealias MPPFile = File
/**
* @suppress
*/
actual val MPPFile.filename: FileName actual val MPPFile.filename: FileName
get() = FileName(name) get() = FileName(name)
/**
* @suppress
*/
actual val MPPFile.filesize: Long actual val MPPFile.filesize: Long
get() = length() get() = length()
/**
* @suppress
*/
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
get() = ::readBytes
/**
* @suppress
*/
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = { get() = {
doInIO { doInIO {

View File

@@ -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)
}

View File

@@ -147,3 +147,10 @@ suspend inline fun <T> runCatchingSafelyWithoutExceptions(
): Result<T?> = runCatching { ): Result<T?> = runCatching {
safelyWithoutExceptions(onException, block) safelyWithoutExceptions(onException, block)
} }
inline fun CoroutineScope(
context: CoroutineContext,
noinline defaultExceptionsHandler: ExceptionHandler<Unit>
) = CoroutineScope(
context + ContextSafelyExceptionHandler(defaultExceptionsHandler)
)

View File

@@ -1,3 +1,6 @@
package dev.inmo.micro_utils.crypto package dev.inmo.micro_utils.crypto
/**
* @suppress
*/
actual fun SourceBytes.md5(): MD5 = CryptoJS.MD5(decodeToString()) actual fun SourceBytes.md5(): MD5 = CryptoJS.MD5(decodeToString())

View File

@@ -3,6 +3,9 @@ package dev.inmo.micro_utils.crypto
import java.math.BigInteger import java.math.BigInteger
import java.security.MessageDigest import java.security.MessageDigest
/**
* @suppress
*/
actual fun SourceBytes.md5(): MD5 = BigInteger( actual fun SourceBytes.md5(): MD5 = BigInteger(
1, 1,
MessageDigest.getInstance("MD5").digest(this) MessageDigest.getInstance("MD5").digest(this)

View File

@@ -13,10 +13,10 @@ repositories {
kotlin { kotlin {
jvm() jvm()
js(IR) { // js(IR) {
browser() // browser()
nodejs() // nodejs()
} // }
android {} android {}
sourceSets { sourceSets {
@@ -29,7 +29,7 @@ kotlin {
it != project it != project
&& it.hasProperty("kotlin") && it.hasProperty("kotlin")
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") } && 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("jvmMain") }
&& it.kotlin.sourceSets.any { it.name.contains("androidMain") } && it.kotlin.sourceSets.any { it.name.contains("androidMain") }
) { ) {
@@ -38,22 +38,22 @@ kotlin {
} }
} }
} }
jsMain { // jsMain {
dependencies { // dependencies {
implementation kotlin('stdlib') // implementation kotlin('stdlib')
project.parent.subprojects.forEach { // project.parent.subprojects.forEach {
if ( // if (
it != project // it != project
&& it.hasProperty("kotlin") // && it.hasProperty("kotlin")
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") } // && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
&& it.kotlin.sourceSets.any { it.name.contains("jsMain") } // && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
) { // ) {
api it // api it
} // }
} // }
} // }
} // }
jvmMain { jvmMain {
dependencies { dependencies {
implementation kotlin('stdlib') implementation kotlin('stdlib')
@@ -116,9 +116,9 @@ tasks.dokkaHtml {
sourceRoots.setFrom(findSourcesWithName("commonMain")) sourceRoots.setFrom(findSourcesWithName("commonMain"))
} }
named("jsMain") { // named("jsMain") {
sourceRoots.setFrom(findSourcesWithName("jsMain", "commonMain")) // sourceRoots.setFrom(findSourcesWithName("jsMain", "commonMain"))
} // }
named("jvmMain") { named("jvmMain") {
sourceRoots.setFrom(findSourcesWithName("jvmMain", "commonMain")) sourceRoots.setFrom(findSourcesWithName("jvmMain", "commonMain"))

View File

@@ -10,6 +10,7 @@ kotlin {
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api project(":micro_utils.common")
api project(":micro_utils.coroutines") api project(":micro_utils.coroutines")
} }
} }

View File

@@ -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
)

View File

@@ -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) }
}
}

View File

@@ -1,5 +1,12 @@
package dev.inmo.micro_utils.fsm.common 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?
} }

View File

@@ -1,46 +1,120 @@
package dev.inmo.micro_utils.fsm.common 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 dev.inmo.micro_utils.coroutines.*
import kotlinx.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, * Default [StatesMachine] may [startChain] and use inside logic for handling [State]s. By default you may use
handlers: List<StateHandlerHolder<out I>> * [DefaultStatesMachine] or build it with [dev.inmo.micro_utils.fsm.common.dsl.buildFSM]. Implementers MUST NOT start
): State? { * handling until [start] method will be called
return handlers.firstOrNull { it.checkHandleable(state) } ?.run { */
handleState(state) 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)
}
}
/**
* Starts handling of [State]s
*/
fun start(scope: CoroutineScope): Job
/**
* 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)
} }
} }
class StatesMachine ( /**
private val statesManager: StatesManager, * Default realization of [StatesMachine]. It uses [statesManager] for incapsulation of [State]s storing and contexts
private val handlers: List<StateHandlerHolder<*>> * resolving, and uses [launchStateHandling] for [State] handling.
) : StatesHandler<State> { *
override suspend fun StatesMachine.handleState(state: State): State? = launchStateHandling(state, handlers) * 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)
fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions { /**
val statePerformer: suspend (State) -> Unit = { state: State -> * This
val newState = launchStateHandling(state, handlers) */
if (newState != null) { protected val statesJobs = mutableMapOf<T, Job>()
statesManager.update(state, newState) protected val statesJobsMutex = Mutex()
} else {
statesManager.endChain(state) protected open suspend fun performUpdate(state: T) {
val newState = launchStateHandling(state, handlers)
if (newState != null) {
statesManager.update(state, newState)
} else {
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) { statesManager.onStartChain.subscribeSafelyWithoutExceptions(this) {
launch { statePerformer(it) } launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) }
} }
statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(this) { statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(this) {
launch { statePerformer(it.second) } launch { performStateUpdate(Optional.presented(it.first), it.second, scope.LinkedSupervisorScope()) }
} }
statesManager.getActiveStates().forEach { 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) statesManager.startChain(state)
} }
} }

View File

@@ -1,92 +1,30 @@
package dev.inmo.micro_utils.fsm.common package dev.inmo.micro_utils.fsm.common
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
interface StatesManager { interface StatesManager<T : State> {
val onChainStateUpdated: Flow<Pair<State, State>> val onChainStateUpdated: Flow<Pair<T, T>>
val onStartChain: Flow<State> val onStartChain: Flow<T>
val onEndChain: Flow<State> val onEndChain: Flow<T>
/** /**
* Must set current set using [State.context] * 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 * Starts chain with [state] as first [State]. May returns false in case of [State.context] of [state] is already
* busy by the other [State] * 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 * Ends chain with context from [state]. In case when [State.context] of [state] is absent, [state] should be just
* ignored * 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()
}

View File

@@ -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)
}
}

View File

@@ -1,35 +1,61 @@
package dev.inmo.micro_utils.fsm.common.dsl package dev.inmo.micro_utils.fsm.common.dsl
import dev.inmo.micro_utils.fsm.common.* 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 import kotlin.reflect.KClass
class FSMBuilder( class FSMBuilder<T : State>(
var statesManager: StatesManager = InMemoryStatesManager() var statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
val fsmBuilder: (statesManager: StatesManager<T>, states: List<CheckableHandlerHolder<T, T>>) -> StatesMachine<T> = { statesManager, states ->
StatesMachine(
statesManager,
states
)
},
var defaultStateHandler: StatesHandler<T, T>? = StatesHandler { null }
) { ) {
private var states = mutableListOf<StateHandlerHolder<*>>() private var states = mutableListOf<CheckableHandlerHolder<T, T>>()
fun <I : State> add(kClass: KClass<I>, handler: StatesHandler<I>) { fun add(handler: CheckableHandlerHolder<T, T>) {
states.add(StateHandlerHolder(kClass, false, handler)) states.add(handler)
} }
fun <I : State> addStrict(kClass: KClass<I>, handler: StatesHandler<I>) { fun <I : T> add(kClass: KClass<I>, handler: StatesHandler<I, T>) {
states.add(StateHandlerHolder(kClass, true, handler)) add(CheckableHandlerHolder(kClass, false, handler))
} }
fun build() = StatesMachine( 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 : T> strictlyOn(handler: StatesHandler<I, T>) {
addStrict(I::class, handler)
}
inline fun <reified I : T> doWhen(
noinline filter: suspend (state: State) -> Boolean,
handler: StatesHandler<I, T>
) {
add(filter, handler)
}
fun build() = fsmBuilder(
statesManager, statesManager,
states.toList() states.toList().let { list ->
defaultStateHandler ?.let { list + it.holder { true } } ?: list
}
) )
} }
inline fun <reified I : State> FSMBuilder.onStateOrSubstate(handler: StatesHandler<I>) { fun <T : State> buildFSM(
add(I::class, handler) block: FSMBuilder<T>.() -> Unit
} ): StatesMachine<T> = FSMBuilder<T>().apply(block).build()
inline fun <reified I : State> FSMBuilder.strictlyOn(handler: StatesHandler<I>) {
addStrict(I::class, handler)
}
fun buildFSM(
block: FSMBuilder.() -> Unit
): StatesMachine = FSMBuilder().apply(block).build()

View File

@@ -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()
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -1,6 +1,7 @@
import dev.inmo.micro_utils.fsm.common.* import dev.inmo.micro_utils.fsm.common.*
import dev.inmo.micro_utils.fsm.common.dsl.buildFSM 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.* import kotlinx.coroutines.*
sealed interface TrafficLightState : State { 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> { strictlyOn<GreenCommon> {
delay(1000L) delay(1000L)
YellowCommon(it.context).also(::println) YellowCommon(it.context).also(::println)

View File

@@ -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)
}

View File

@@ -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
)
)

View File

@@ -7,29 +7,29 @@ android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
org.gradle.jvmargs=-Xmx2g org.gradle.jvmargs=-Xmx2g
kotlin_version=1.5.21 kotlin_version=1.6.10
kotlin_coroutines_version=1.5.1 kotlin_coroutines_version=1.6.0
kotlin_serialisation_core_version=1.2.2 kotlin_serialisation_core_version=1.3.2
kotlin_exposed_version=0.32.1 kotlin_exposed_version=0.37.2
ktor_version=1.6.2 ktor_version=1.6.7
klockVersion=2.3.1 klockVersion=2.4.12
github_release_plugin_version=2.2.12 github_release_plugin_version=2.2.12
uuidVersion=0.3.0 uuidVersion=0.3.1
# ANDROID # ANDROID
core_ktx_version=1.6.0 core_ktx_version=1.7.0
androidx_recycler_version=1.2.1 androidx_recycler_version=1.2.1
appcompat_version=1.3.0 appcompat_version=1.4.0
android_minSdkVersion=19 android_minSdkVersion=19
android_compileSdkVersion=30 android_compileSdkVersion=32
android_buildToolsVersion=30.0.3 android_buildToolsVersion=32.0.0
dexcount_version=2.1.0-RC01 dexcount_version=3.0.1
junit_version=4.12 junit_version=4.12
test_ext_junit_version=1.1.2 test_ext_junit_version=1.1.2
espresso_core=3.3.0 espresso_core=3.3.0
@@ -40,10 +40,10 @@ crypto_js_version=4.1.1
# Dokka # Dokka
dokka_version=1.4.32 dokka_version=1.6.0
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.5.17 version=0.9.2
android_code_version=58 android_code_version=92

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -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

View File

@@ -1,16 +1,20 @@
package dev.inmo.micro_utils.ktor.client 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 dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.request.get import io.ktor.client.request.*
import io.ktor.client.request.post import io.ktor.client.request.forms.*
import io.ktor.http.*
import io.ktor.utils.io.core.ByteReadPacket
import kotlinx.serialization.* import kotlinx.serialization.*
typealias BodyPair<T> = Pair<SerializationStrategy<T>, T> typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
class UnifiedRequester( class UnifiedRequester(
private val client: HttpClient = HttpClient(), val client: HttpClient = HttpClient(),
private val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) { ) {
suspend fun <ResultType> uniget( suspend fun <ResultType> uniget(
url: String, url: String,
@@ -31,6 +35,54 @@ class UnifiedRequester(
resultDeserializer: DeserializationStrategy<ResultType> resultDeserializer: DeserializationStrategy<ResultType>
) = client.unipost(url, bodyInfo, resultDeserializer, serialFormat) ) = 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( fun <T> createStandardWebsocketFlow(
url: String, url: String,
checkReconnection: (Throwable?) -> Boolean = { true }, checkReconnection: (Throwable?) -> Boolean = { true },
@@ -69,3 +121,124 @@ suspend fun <BodyType, ResultType> HttpClient.unipost(
}.let { }.let {
serialFormat.decodeDefault(resultDeserializer, it) 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
)

View File

@@ -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)
}
}

View File

@@ -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()
}

View File

@@ -10,6 +10,7 @@ kotlin {
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api internalProject("micro_utils.common")
api "org.jetbrains.kotlinx:kotlinx-serialization-cbor:$kotlin_serialisation_core_version" api "org.jetbrains.kotlinx:kotlinx-serialization-cbor:$kotlin_serialisation_core_version"
api "com.soywiz.korlibs.klock:klock:$klockVersion" api "com.soywiz.korlibs.klock:klock:$klockVersion"
} }

View File

@@ -1,22 +1,31 @@
package dev.inmo.micro_utils.ktor.server 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.coroutines.safely
import dev.inmo.micro_utils.ktor.common.* import dev.inmo.micro_utils.ktor.common.*
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode 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.receive
import io.ktor.request.receiveMultipart
import io.ktor.response.respond import io.ktor.response.respond
import io.ktor.response.respondBytes import io.ktor.response.respondBytes
import io.ktor.routing.Route 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.util.pipeline.PipelineContext
import io.ktor.utils.io.core.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.* import kotlinx.serialization.*
import java.io.File
import java.io.File.createTempFile
class UnifiedRouter( class UnifiedRouter(
private val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat, val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
private val serialFormatContentType: ContentType = standardKtorSerialFormatContentType val serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) { ) {
fun <T> Route.includeWebsocketHandling( fun <T> Route.includeWebsocketHandling(
suburl: String, 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( suspend fun ApplicationCall.getParameterOrSendError(
field: String field: String
) = parameters[field].also { ) = parameters[field].also {

View File

@@ -1,5 +1,6 @@
package dev.inmo.micro_utils.ktor.server package dev.inmo.micro_utils.ktor.server
import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator
import io.ktor.application.Application import io.ktor.application.Application
import io.ktor.server.cio.CIO import io.ktor.server.cio.CIO
import io.ktor.server.engine.* import io.ktor.server.engine.*
@@ -31,3 +32,27 @@ fun createKtorServer(
port: Int = Random.nextInt(1024, 65535), port: Int = Random.nextInt(1024, 65535),
block: Application.() -> Unit block: Application.() -> Unit
): ApplicationEngine = createKtorServer(CIO, host, port, block) ): 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)

View File

@@ -0,0 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"

View 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
}

View 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))
}
}

View File

@@ -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)
}
}

View File

@@ -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()

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.language_codes"/>

View 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)

View File

@@ -1,3 +1,5 @@
@file:Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
package dev.inmo.micro_utils.mime_types package dev.inmo.micro_utils.mime_types
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@@ -24,3 +24,8 @@ kotlin {
} }
apply from: "$defaultAndroidSettingsPresetPath" apply from: "$defaultAndroidSettingsPresetPath"
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -4,7 +4,13 @@ project.group = "$group"
apply from: "$publishGradlePath" apply from: "$publishGradlePath"
kotlin { kotlin {
jvm() jvm {
compilations.main {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
sourceSets { sourceSets {
commonMain { commonMain {
@@ -26,3 +32,8 @@ kotlin {
} }
} }
} }
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -4,7 +4,13 @@ project.group = "$group"
apply from: "$publishGradlePath" apply from: "$publishGradlePath"
kotlin { kotlin {
jvm() jvm {
compilations.main {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
js (IR) { js (IR) {
browser() browser()
nodejs() nodejs()
@@ -48,3 +54,8 @@ kotlin {
} }
apply from: "$defaultAndroidSettingsPresetPath" apply from: "$defaultAndroidSettingsPresetPath"
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -5,3 +5,13 @@ plugins {
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":micro_utils.common")
}
}
}
}

View File

@@ -1,5 +1,6 @@
package dev.inmo.micro_utils.pagination package dev.inmo.micro_utils.pagination
import dev.inmo.micro_utils.common.intersect
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.floor 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 * If you want to request something, you should use [SimplePagination]. If you need to return some result including
* pagination - [PaginationResult] * pagination - [PaginationResult]
*/ */
interface Pagination { interface Pagination : ClosedRange<Int> {
/** /**
* Started with 0. * Started with 0.
* Number of page inside of pagination. Offset can be calculated as [page] * [size] * 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] * Size of current page. Offset can be calculated as [page] * [size]
*/ */
val size: Int 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 * First number in index of objects. It can be used as offset for databases or other data sources
*/ */
val Pagination.firstIndex: Int 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: * 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 * you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19. Here [Pagination.lastIndexExclusive] == 20
*/ */
val Pagination.lastIndexExclusive: Int 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: * 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. * you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19.
*/ */
val Pagination.lastIndex: Int val Pagination.lastIndex: Int
get() = lastIndexExclusive - 1 get() = endInclusive
/** /**
* Calculates pages count for given [datasetSize] * Calculates pages count for given [datasetSize]

View File

@@ -16,6 +16,16 @@ suspend fun <T> getAll(
return results.toList() 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( suspend fun <T> getAllWithNextPaging(
initialPagination: Pagination = FirstPagePagination(), initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T> block: suspend (Pagination) -> PaginationResult<T>
@@ -25,6 +35,14 @@ suspend fun <T> getAllWithNextPaging(
block 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( suspend fun <T> getAllWithCurrentPaging(
initialPagination: Pagination = FirstPagePagination(), initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T> block: suspend (Pagination) -> PaginationResult<T>
@@ -33,3 +51,11 @@ suspend fun <T> getAllWithCurrentPaging(
{ it.currentPageIfNotEmpty() }, { it.currentPageIfNotEmpty() },
block block
) )
suspend fun <T, R> R.getAllByWithCurrentPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend R.(Pagination) -> PaginationResult<T>
): List<T> = getAllWithCurrentPaging(
initialPagination,
{ block(it) }
)

View File

@@ -1,5 +1,4 @@
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
apply plugin: 'signing'
task javadocsJar(type: Jar) { task javadocsJar(type: Jar) {
classifier = 'javadoc' classifier = 'javadoc'
@@ -69,8 +68,19 @@ publishing {
} }
} }
} }
signing { if (project.hasProperty("signing.gnupg.keyName")) {
useGpgCmd() apply plugin: 'signing'
sign publishing.publications
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)
}
}
} }

View File

@@ -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"}}}

View File

@@ -2,6 +2,8 @@ package dev.inmo.micro_utils.repos
import android.database.Cursor import android.database.Cursor
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import androidx.core.database.*
import dev.inmo.micro_utils.repos.getLongOrNull
fun createTableQuery( fun createTableQuery(
tableName: String, tableName: String,
@@ -32,6 +34,11 @@ fun SQLiteDatabase.createTable(
fun Cursor.getString(columnName: String): String = getString( fun Cursor.getString(columnName: String): String = getString(
getColumnIndexOrThrow(columnName) getColumnIndexOrThrow(columnName)
) )
fun Cursor.getStringOrNull(columnName: String): String? {
return getStringOrNull(
getColumnIndex(columnName).takeIf { it != -1 } ?: return null
)
}
/** /**
* @throws IllegalArgumentException * @throws IllegalArgumentException
@@ -39,6 +46,11 @@ fun Cursor.getString(columnName: String): String = getString(
fun Cursor.getShort(columnName: String): Short = getShort( fun Cursor.getShort(columnName: String): Short = getShort(
getColumnIndexOrThrow(columnName) getColumnIndexOrThrow(columnName)
) )
fun Cursor.getShortOrNull(columnName: String): Short? {
return getShortOrNull(
getColumnIndex(columnName).takeIf { it != -1 } ?: return null
)
}
/** /**
* @throws IllegalArgumentException * @throws IllegalArgumentException
@@ -46,6 +58,11 @@ fun Cursor.getShort(columnName: String): Short = getShort(
fun Cursor.getLong(columnName: String): Long = getLong( fun Cursor.getLong(columnName: String): Long = getLong(
getColumnIndexOrThrow(columnName) getColumnIndexOrThrow(columnName)
) )
fun Cursor.getLongOrNull(columnName: String): Long? {
return getLongOrNull(
getColumnIndex(columnName).takeIf { it != -1 } ?: return null
)
}
/** /**
* @throws IllegalArgumentException * @throws IllegalArgumentException
@@ -53,6 +70,23 @@ fun Cursor.getLong(columnName: String): Long = getLong(
fun Cursor.getInt(columnName: String): Int = getInt( fun Cursor.getInt(columnName: String): Int = getInt(
getColumnIndexOrThrow(columnName) 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 * @throws IllegalArgumentException
@@ -60,6 +94,11 @@ fun Cursor.getInt(columnName: String): Int = getInt(
fun Cursor.getDouble(columnName: String): Double = getDouble( fun Cursor.getDouble(columnName: String): Double = getDouble(
getColumnIndexOrThrow(columnName) getColumnIndexOrThrow(columnName)
) )
fun Cursor.getDoubleOrNull(columnName: String): Double? {
return getDoubleOrNull(
getColumnIndex(columnName).takeIf { it != -1 } ?: return null
)
}
fun SQLiteDatabase.select( fun SQLiteDatabase.select(
table: String, table: String,

View File

@@ -6,13 +6,15 @@ import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>( abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>(
helper: StandardSQLHelper helper: StandardSQLHelper,
replyInFlows: Int = 0,
extraBufferCapacityInFlows: Int = 64
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>, ) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>,
AbstractAndroidCRUDRepo<ObjectType, IdType>(helper), AbstractAndroidCRUDRepo<ObjectType, IdType>(helper),
StandardCRUDRepo<ObjectType, IdType, InputValueType> { StandardCRUDRepo<ObjectType, IdType, InputValueType> {
protected val newObjectsChannel = MutableSharedFlow<ObjectType>(64) protected val newObjectsChannel = MutableSharedFlow<ObjectType>(replyInFlows, extraBufferCapacityInFlows)
protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(64) protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(replyInFlows, extraBufferCapacityInFlows)
protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(64) protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(replyInFlows, extraBufferCapacityInFlows)
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asSharedFlow() override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asSharedFlow()
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow() override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow()
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asSharedFlow() override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asSharedFlow()
@@ -102,4 +104,4 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
} }
override suspend fun count(): Long = helper.blockingReadableTransaction { select(tableName).use { it.count.toLong() } } override suspend fun count(): Long = helper.blockingReadableTransaction { select(tableName).use { it.count.toLong() } }
} }

View File

@@ -10,15 +10,16 @@ import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>( abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 0, flowsChannelsSize: Int = 0,
tableName: String = "" tableName: String = "",
replyCacheInFlows: Int = 0
) : ) :
AbstractExposedReadCRUDRepo<ObjectType, IdType>(tableName), AbstractExposedReadCRUDRepo<ObjectType, IdType>(tableName),
ExposedCRUDRepo<ObjectType, IdType>, ExposedCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
{ {
protected val newObjectsChannel = MutableSharedFlow<ObjectType>(flowsChannelsSize) protected val newObjectsChannel = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(flowsChannelsSize) protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(flowsChannelsSize) protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(replyCacheInFlows, flowsChannelsSize)
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asSharedFlow() override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asSharedFlow()
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow() override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow()

View File

@@ -19,8 +19,8 @@ open class ExposedKeyValueRepo<Key, Value>(
valueColumnAllocator, valueColumnAllocator,
tableName tableName
) { ) {
private val _onNewValue = MutableSharedFlow<Pair<Key, Value>>() protected val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
private val _onValueRemoved = MutableSharedFlow<Key>() protected val _onValueRemoved = MutableSharedFlow<Key>()
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow() override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow() override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()

View File

@@ -12,8 +12,8 @@ open class ExposedReadKeyValueRepo<Key, Value>(
valueColumnAllocator: ColumnAllocator<Value>, valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null tableName: String? = null
) : ReadStandardKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") { ) : ReadStandardKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
protected val keyColumn: Column<Key> = keyColumnAllocator() val keyColumn: Column<Key> = keyColumnAllocator()
protected val valueColumn: Column<Value> = valueColumnAllocator() val valueColumn: Column<Value> = valueColumnAllocator()
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn) override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
init { initTable() } init { initTable() }

View File

@@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
typealias ExposedKeyValuesRepo<Key, Value> = ExposedOneToManyKeyValueRepo<Key, Value>
open class ExposedOneToManyKeyValueRepo<Key, Value>( open class ExposedOneToManyKeyValueRepo<Key, Value>(
database: Database, database: Database,
keyColumnAllocator: ColumnAllocator<Key>, keyColumnAllocator: ColumnAllocator<Key>,

View File

@@ -3,17 +3,20 @@ package dev.inmo.micro_utils.repos.exposed.onetomany
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.* 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.*
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
typealias ExposedReadKeyValuesRepo<Key, Value> = ExposedReadOneToManyKeyValueRepo<Key, Value>
open class ExposedReadOneToManyKeyValueRepo<Key, Value>( open class ExposedReadOneToManyKeyValueRepo<Key, Value>(
override val database: Database, override val database: Database,
keyColumnAllocator: ColumnAllocator<Key>, keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>, valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null tableName: String? = null
) : ReadOneToManyKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") { ) : ReadOneToManyKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
protected val keyColumn: Column<Key> = keyColumnAllocator() val keyColumn: Column<Key> = keyColumnAllocator()
protected val valueColumn: Column<Value> = valueColumnAllocator() val valueColumn: Column<Value> = valueColumnAllocator()
init { initTable() } init { initTable() }

View File

@@ -18,8 +18,8 @@ inline fun versionsRepo(database: Database): VersionsRepo<Database> = StandardVe
class ExposedStandardVersionsRepoProxy( class ExposedStandardVersionsRepoProxy(
override val database: Database override val database: Database
) : StandardVersionsRepoProxy<Database>, Table("ExposedVersionsProxy"), ExposedRepo { ) : StandardVersionsRepoProxy<Database>, Table("ExposedVersionsProxy"), ExposedRepo {
private val tableNameColumn = text("tableName") val tableNameColumn = text("tableName")
private val tableVersionColumn = integer("tableName") val tableVersionColumn = integer("tableName")
init { init {
initTable() initTable()

View File

@@ -23,13 +23,13 @@ class ReadMapCRUDRepo<ObjectType, IdType>(
} }
abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>( abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(
private val map: MutableMap<IdType, ObjectType> = mutableMapOf() protected val map: MutableMap<IdType, ObjectType> = mutableMapOf()
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> { ) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> {
private val _newObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow() protected val _newObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow()
override val newObjectsFlow: Flow<ObjectType> = _newObjectsFlow.asSharedFlow() 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() 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() override val deletedObjectsIdsFlow: Flow<IdType> = _deletedObjectsIdsFlow.asSharedFlow()
protected abstract suspend fun updateObject(newValue: InputValueType, id: IdType, old: ObjectType): ObjectType protected abstract suspend fun updateObject(newValue: InputValueType, id: IdType, old: ObjectType): ObjectType

View File

@@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
class ReadMapKeyValueRepo<Key, Value>( class ReadMapKeyValueRepo<Key, Value>(
private val map: Map<Key, Value> = emptyMap() protected val map: Map<Key, Value> = emptyMap()
) : ReadStandardKeyValueRepo<Key, Value> { ) : ReadStandardKeyValueRepo<Key, Value> {
override suspend fun get(k: Key): Value? = map[k] override suspend fun get(k: Key): Value? = map[k]

View File

@@ -8,20 +8,20 @@ import kotlin.reflect.KClass
open class TypedSerializer<T : Any>( open class TypedSerializer<T : Any>(
kClass: KClass<T>, kClass: KClass<T>,
presetSerializers: Map<String, KSerializer<out T>> = emptyMap() presetSerializers: Map<String, KSerializer<out T>> = emptyMap(),
) : KSerializer<T> { ) : KSerializer<T> {
protected val serializers = presetSerializers.toMutableMap() protected val serializers = presetSerializers.toMutableMap()
@InternalSerializationApi @OptIn(InternalSerializationApi::class)
open override val descriptor: SerialDescriptor = buildSerialDescriptor( override val descriptor: SerialDescriptor = buildSerialDescriptor(
"TextSourceSerializer", "TypedSerializer",
SerialKind.CONTEXTUAL SerialKind.CONTEXTUAL
) { ) {
element("type", String.serializer().descriptor) element("type", String.serializer().descriptor)
element("value", ContextualSerializer(kClass).descriptor) element("value", ContextualSerializer(kClass).descriptor)
} }
@InternalSerializationApi @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
open override fun deserialize(decoder: Decoder): T { override fun deserialize(decoder: Decoder): T {
return decoder.decodeStructure(descriptor) { return decoder.decodeStructure(descriptor) {
var type: String? = null var type: String? = null
lateinit var result: T 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) { protected open fun <O: T> CompositeEncoder.encode(value: O) {
encodeSerializableElement(descriptor, 1, value::class.serializer() as KSerializer<O>, value) encodeSerializableElement(descriptor, 1, value::class.serializer() as KSerializer<O>, value)
} }
@InternalSerializationApi @OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
open override fun serialize(encoder: Encoder, value: T) { override fun serialize(encoder: Encoder, value: T) {
encoder.encodeStructure(descriptor) { encoder.encodeStructure(descriptor) {
val valueSerializer = value::class.serializer() val valueSerializer = value::class.serializer()
val type = serializers.keys.first { serializers[it] == valueSerializer } 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( inline fun <reified T : Any> TypedSerializer(
presetSerializers: Map<String, KSerializer<out T>> = emptyMap() presetSerializers: Map<String, KSerializer<out T>> = emptyMap()
) = TypedSerializer(T::class, presetSerializers) ) = TypedSerializer(T::class, presetSerializers)
inline fun <reified T : Any> TypedSerializer(
vararg presetSerializers: Pair<String, KSerializer<out T>>
) = TypedSerializer(presetSerializers.toMap())

View File

@@ -10,6 +10,8 @@ String[] includes = [
":pagination:ktor:common", ":pagination:ktor:common",
":pagination:ktor:server", ":pagination:ktor:server",
":mime_types", ":mime_types",
":language_codes",
":language_codes:generator",
":repos:common", ":repos:common",
":repos:cache", ":repos:cache",
":repos:exposed", ":repos:exposed",