Compare commits

..
Author SHA1 Message Date
root 940ee3df06 Revert "Update ksp to v1.8.21-1.0.11" 2023-04-28 00:34:10 +06:00
InsanusMokrassar 2e7bab10fd Merge pull request #245 from InsanusMokrassar/renovate/ksp
Update ksp to v1.8.21-1.0.11
2023-04-28 00:14:25 +06:00
renovate[bot] 3ed70a37ea Update ksp to v1.8.21-1.0.11 2023-04-27 18:10:41 +00:00
InsanusMokrassar fe8f80b9d9 Merge pull request #243 from InsanusMokrassar/0.18.0
0.18.0
2023-04-27 17:08:42 +06:00
InsanusMokrassar d81fb32fb9 remove previous deprecations 2023-04-27 16:29:08 +06:00
InsanusMokrassar 2877b5532c Revert "Get back some functional of "trying to update up to gradle wrapper 8.1.1""
This reverts commit b938b21395.
2023-04-27 16:24:58 +06:00
InsanusMokrassar b938b21395 Get back some functional of "trying to update up to gradle wrapper 8.1.1" 2023-04-27 16:24:37 +06:00
InsanusMokrassar 58836359cc Revert "trying to update up to gradle wrapper 8.1.1"
This reverts commit 5edb0e1331.
2023-04-27 15:00:43 +06:00
InsanusMokrassar 5edb0e1331 trying to update up to gradle wrapper 8.1.1 2023-04-27 14:15:45 +06:00
InsanusMokrassar 0f0d0b5d58 update android fragments dependency 2023-04-27 12:16:48 +06:00
InsanusMokrassar 46c1887cbe remove usage of cache updates flows in autorecached repos 2023-04-27 12:06:50 +06:00
InsanusMokrassar 5f231c2212 ReadKeyValuesRepo#removeWithValue 2023-04-25 19:32:43 +06:00
InsanusMokrassar 4e97ce86aa add defauult value to kvCache in fullyCached 2023-04-25 17:39:14 +06:00
InsanusMokrassar 315a7cb29e Rename full caching factories functions to fullyCached 2023-04-25 17:23:47 +06:00
InsanusMokrassar aa7cc503f2 improvements in query parameters getting 2023-04-25 13:14:12 +06:00
InsanusMokrassar 4bbe7e5a80 update build tools version 2023-04-25 12:23:53 +06:00
InsanusMokrassar d9c05f38d2 start 0.18.0 2023-04-25 12:23:08 +06:00
InsanusMokrassar cd0c4c9650 Merge pull request #237 from InsanusMokrassar/renovate/android-gradle
Update dependency com.android.tools.build:gradle to v7.4.2
2023-04-25 00:38:47 +06:00
InsanusMokrassar fc3407f104 Merge pull request #241 from InsanusMokrassar/0.17.8
0.17.8
2023-04-19 20:16:36 +06:00
InsanusMokrassar 3a5544206b update ktor 2023-04-19 19:46:39 +06:00
InsanusMokrassar e17e2f7fb8 start 0.17.8 2023-04-19 19:45:58 +06:00
renovate[bot] d32c95f143 Update dependency com.android.tools.build:gradle to v7.4.2 2023-04-18 18:12:52 +00:00
InsanusMokrassar 6d8a8ab018 Merge pull request #233 from InsanusMokrassar/0.17.7
0.17.7
2023-04-18 19:37:06 +06:00
InsanusMokrassar a7dce8fa78 update dependencies 2023-04-18 19:24:05 +06:00
InsanusMokrassar ca73ff8e19 add support of native in startup 2023-04-18 19:20:39 +06:00
InsanusMokrassar d01ad10d7d start 0.17.7 2023-04-18 19:20:39 +06:00
InsanusMokrassar 81041ee43c Merge pull request #232 from InsanusMokrassar/renovate/configure
Configure Renovate
2023-04-18 19:20:15 +06:00
renovate[bot] 6e004c2ae4 Add renovate.json 2023-04-18 08:23:10 +00:00
InsanusMokrassar 0e2fac5b22 Merge pull request #231 from InsanusMokrassar/0.17.6
0.17.6
2023-04-13 11:27:18 +06:00
InsanusMokrassar 269da7f155 update dependencies and fill changelog 2023-04-13 11:20:34 +06:00
InsanusMokrassar 3cb6b73ee0 add preloadImage 2023-04-07 01:17:04 +06:00
InsanusMokrassar a938ee1efb simplify API of MPPFile.input 2023-04-04 01:26:18 +06:00
InsanusMokrassar 6ea5e2e5a6 update version 2023-04-03 22:54:57 +06:00
InsanusMokrassar 617dfb54e0 experimentally add linuxx64 and mingwx64 as target platforms 2023-04-03 22:35:41 +06:00
InsanusMokrassar d23e005985 preview of files realization 2023-04-03 15:49:46 +06:00
InsanusMokrassar e5207f5bc5 Revert "Revert "trying to add native support""
This reverts commit c96cea8db0.
2023-04-03 10:26:03 +06:00
InsanusMokrassar c96cea8db0 Revert "trying to add native support"
This reverts commit 0a8e71d76a.
2023-04-03 10:22:53 +06:00
InsanusMokrassar 0a8e71d76a trying to add native support 2023-04-03 10:22:43 +06:00
InsanusMokrassar cf1fd32b08 Merge pull request #230 from InsanusMokrassar/0.17.5
0.17.5
2023-03-10 22:30:32 +06:00
InsanusMokrassar cc4224ea1f update kv and crud tests 2023-03-10 22:08:35 +06:00
InsanusMokrassar f4c148bc58 add map type info in ktor read kv and crud repos 2023-03-10 21:41:01 +06:00
InsanusMokrassar 022297ad3f fixes in AutoRecacheReadKeyValueRepo 2023-03-10 19:37:36 +06:00
InsanusMokrassar 5180d6fc3e hotfixes 2023-03-10 18:43:43 +06:00
InsanusMokrassar eeebbff70d getAll 2023-03-10 18:37:48 +06:00
InsanusMokrassar afc6aeea15 fixes 2023-03-10 16:14:51 +06:00
InsanusMokrassar 486515eddd update dokka 2023-03-10 16:09:33 +06:00
InsanusMokrassar 0e21699cd1 several updates 2023-03-10 15:45:02 +06:00
InsanusMokrassar f1678ef7cf start 0.17.5 2023-03-10 15:36:36 +06:00
InsanusMokrassar cea65fc76e Merge pull request #229 from InsanusMokrassar/0.17.4
0.17.4
2023-03-09 23:23:55 +06:00
InsanusMokrassar c9e320b72a update compose version 2023-03-09 23:22:33 +06:00
InsanusMokrassar 555956087d add android manifest to mapper module 2023-03-09 22:23:56 +06:00
InsanusMokrassar b3f468f901 add docs to mapper serialization and allow to use them as supertypes 2023-03-09 22:21:22 +06:00
InsanusMokrassar f5f7511781 add mapper serializer 2023-03-09 21:55:07 +06:00
InsanusMokrassar 4be1d93f60 start 0.17.4 2023-03-09 21:49:15 +06:00
InsanusMokrassar 7d684608ef Merge pull request #228 from InsanusMokrassar/0.17.3
0.17.3
2023-03-07 23:30:23 +06:00
InsanusMokrassar 2c7fd320eb fixed 2023-03-07 22:28:56 +06:00
InsanusMokrassar 88ee82e1c6 add Diff#isEmpty 2023-03-07 21:50:23 +06:00
InsanusMokrassar d6402c624e optimization of nonstrict comparison 2023-03-07 19:14:43 +06:00
InsanusMokrassar 8b9c93bc10 diffs improvement 2023-03-07 19:12:12 +06:00
InsanusMokrassar 4f5e261d01 start 0.17.3 2023-03-07 18:59:34 +06:00
InsanusMokrassar cf455aebe6 Merge pull request #227 from InsanusMokrassar/0.17.2
0.17.2
2023-03-02 21:57:49 +06:00
InsanusMokrassar 1380d5f8e1 fill changelog 2023-03-02 21:38:20 +06:00
InsanusMokrassar 5f65260bfe Update DefaultStatesManager.kt 2023-03-02 21:35:59 +06:00
InsanusMokrassar 11f0dcfc01 Update DefaultStatesManager.kt 2023-03-02 21:32:58 +06:00
InsanusMokrassar 555b7886a4 Update gradle.properties 2023-03-02 21:28:50 +06:00
InsanusMokrassar 3707a6c0ce Merge pull request #226 from InsanusMokrassar/0.17.1
0.17.1
2023-02-28 19:36:08 +06:00
InsanusMokrassar 4c8d92b4c3 update ktor 2023-02-28 19:32:32 +06:00
InsanusMokrassar 8bbd33c896 now all android modules depends on jvm 2023-02-28 19:08:39 +06:00
InsanusMokrassar ac33a3580f start 0.17.1 2023-02-28 19:04:04 +06:00
InsanusMokrassar a64a32fbe6 Merge pull request #225 from InsanusMokrassar/0.17.0
0.17.0
2023-02-28 12:15:41 +06:00
InsanusMokrassar 9493e97a38 remove redundant part from defaultAndroidSettings.gradle 2023-02-27 22:46:08 +06:00
InsanusMokrassar 88bd770260 fill dependencies updates changelog 2023-02-27 22:45:35 +06:00
InsanusMokrassar a7bd33b7bf update kotlin and ksp versions 2023-02-27 17:53:37 +06:00
InsanusMokrassar 73c724a2e5 small cleanup in build scripts 2023-02-27 17:03:09 +06:00
InsanusMokrassar d8cf3c6726 update dependencies and at least it is buildable 2023-02-27 16:56:39 +06:00
InsanusMokrassar 15dee238b5 start 0.17.0 2023-02-27 15:54:37 +06:00
InsanusMokrassar c70626734e Merge pull request #224 from InsanusMokrassar/0.16.13
0.16.13
2023-02-27 15:51:20 +06:00
InsanusMokrassar 5a765ea1bc get back publishing signing 2023-02-27 15:43:52 +06:00
InsanusMokrassar 8215f9d2c6 temporal solution of generating problem 2023-02-26 14:37:41 +06:00
InsanusMokrassar d2e6d2ec80 generators for models has been created 2023-02-25 19:56:12 +06:00
InsanusMokrassar 3718181e8f start 0.16.13 2023-02-25 18:44:07 +06:00
InsanusMokrassar 0d825cf424 Merge pull request #223 from InsanusMokrassar/0.16.12
0.16.12
2023-02-22 10:30:45 +06:00
InsanusMokrassar 28a804d988 improve start launcher plugin logs 2023-02-22 10:28:25 +06:00
InsanusMokrassar 9e4bb9d678 add readme to generator and add several fixes in processor 2023-02-22 10:20:15 +06:00
InsanusMokrassar 9c40d7da3d add generator for koin definitions 2023-02-22 01:34:42 +06:00
InsanusMokrassar 2b76ad0aa9 improvements in selectByIds of CommonExposedRepo 2023-02-20 11:55:14 +06:00
InsanusMokrassar e4b619e050 start 0.16.12 2023-02-20 11:54:26 +06:00
InsanusMokrassar 36c09feaf2 Merge pull request #222 from InsanusMokrassar/0.16.11
0.16.11
2023-02-20 00:49:12 +06:00
InsanusMokrassar 2d68321503 Update build.gradle 2023-02-20 00:19:27 +06:00
InsanusMokrassar 85455ab21c improvements in language codes 2023-02-19 19:34:42 +06:00
InsanusMokrassar 18d63eb980 start 0.16.11 2023-02-19 19:20:03 +06:00
InsanusMokrassar 2e429e9704 Merge pull request #221 from InsanusMokrassar/0.16.10
0.16.10
2023-02-17 13:54:09 +06:00
InsanusMokrassar f4af28059b improvements in ReadKeyValueFromCRUDRepo 2023-02-17 13:45:22 +06:00
InsanusMokrassar c1476bd075 add ReadCRUDFromKeyValueRepo 2023-02-15 20:16:54 +06:00
InsanusMokrassar 16c720fddd start 0.16.10 2023-02-15 20:14:55 +06:00
InsanusMokrassar 8b4b4a5eca Merge pull request #220 from InsanusMokrassar/0.16.9
0.16.9
2023-02-13 16:09:55 +06:00
InsanusMokrassar 32e6e5b7e2 Update CHANGELOG.md 2023-02-13 16:09:44 +06:00
InsanusMokrassar a9f7fd8e32 new extension HttpResponse.bodyOrNullOnNoContent 2023-02-10 15:17:08 +06:00
InsanusMokrassar 95be1a26f2 improvements in HttpResponse.bodyOrNull 2023-02-10 15:14:03 +06:00
InsanusMokrassar ef9b31aee0 start 0.16.9 2023-02-05 18:17:20 +06:00
InsanusMokrassar df3c01ff0a Merge pull request #219 from InsanusMokrassar/0.16.8
0.16.8
2023-02-04 01:46:43 +06:00
InsanusMokrassar 4704c5a33d Update CHANGELOG.md 2023-02-04 01:39:49 +06:00
InsanusMokrassar 225c06550a fixes in FileKeyValueRepo 2023-02-03 10:23:52 +06:00
InsanusMokrassar f0987614c6 fixes in FileKeyValueRepo 2023-02-03 10:07:24 +06:00
InsanusMokrassar 269c2876f3 actualize all make it possible to disable clear on actualization 2023-02-02 20:07:31 +06:00
InsanusMokrassar 168d6acf7c actualizeAll now is overridable in auto recache repos 2023-02-02 20:05:25 +06:00
InsanusMokrassar a5f718e257 add meppers for kv<->kvs repos 2023-02-02 12:29:00 +06:00
InsanusMokrassar 4f68459582 start add kv to kvs transformations 2023-02-02 10:15:37 +06:00
InsanusMokrassar 442db122cf remove redundant id getter in keyvalues autocached repo 2023-02-01 23:02:06 +06:00
InsanusMokrassar 580d757be2 fixes in download file and upgrade ktor version 2023-02-01 22:28:17 +06:00
InsanusMokrassar 47b0f6d2d8 fixes 2023-02-01 13:03:51 +06:00
InsanusMokrassar 3f6f6ebc2b realize invalidate method in fallback cache repos 2023-01-31 10:09:55 +06:00
InsanusMokrassar 2645ea29d6 Update ActionWrapper.kt 2023-01-31 00:11:45 +06:00
InsanusMokrassar 79f2041565 Update AutoRecacheReadCRUDRepo.kt 2023-01-30 23:59:48 +06:00
InsanusMokrassar 4a7567f288 Update AutoRecacheWriteCRUDRepo.kt 2023-01-30 23:59:19 +06:00
InsanusMokrassar 8a890ed6ed Update AutoRecacheReadKeyValueRepo.kt 2023-01-30 23:58:43 +06:00
InsanusMokrassar 3d90df6897 Update AutoRecacheWriteKeyValueRepo.kt 2023-01-30 23:58:11 +06:00
InsanusMokrassar 681c13144a Update AutoRecacheReadKeyValuesRepo.kt 2023-01-30 23:57:40 +06:00
InsanusMokrassar b64f2e6d32 Update AutoRecacheWriteKeyValuesRepo.kt 2023-01-30 23:56:57 +06:00
InsanusMokrassar 428eabb1bd Create FallbackCacheRepo.kt 2023-01-30 23:54:32 +06:00
InsanusMokrassar 2162e83bce fixes in actualizeAll 2023-01-29 22:51:39 +06:00
InsanusMokrassar 6142022283 improve actualizeAll to lazily clear repo 2023-01-29 22:46:32 +06:00
InsanusMokrassar e6d9c8250f add all autorecaches repos 2023-01-29 22:18:22 +06:00
InsanusMokrassar 46178e723b fixes 2023-01-29 20:54:32 +06:00
InsanusMokrassar 605f55acd2 fixes in Write* cache repos 2023-01-29 20:35:10 +06:00
InsanusMokrassar 0f8b69aa60 start 0.16.8 2023-01-29 20:35:10 +06:00
InsanusMokrassar 551d8ec480 Merge pull request #218 from InsanusMokrassar/0.16.7
0.16.7
2023-01-29 13:20:16 +06:00
InsanusMokrassar fc48446ec4 temporarily remove fallback repo 2023-01-29 13:15:56 +06:00
InsanusMokrassar 3644b83ac6 fill changelog 2023-01-29 13:08:41 +06:00
InsanusMokrassar cd73791b6f add docs to the MutableState.asState 2023-01-29 12:46:27 +06:00
InsanusMokrassar 03de71df2e add docs to as compose state 2023-01-29 12:43:51 +06:00
InsanusMokrassar 83d5d3faf4 improve flow as state functionality 2023-01-29 12:28:22 +06:00
InsanusMokrassar 0c8bec4c89 add extension actualizeAll 2023-01-27 15:15:27 +06:00
InsanusMokrassar 7fc93817c1 start to add fallback repos 2023-01-27 14:45:31 +06:00
InsanusMokrassar d0a00031a1 Update CHANGELOG.md 2023-01-26 22:54:17 +06:00
InsanusMokrassar 6ebc5aa0c2 potential fix of state value change in asMutableComposeState 2023-01-23 15:21:47 +06:00
InsanusMokrassar 8a6b4bb49e add alsoIfTrue/alsoIfFalse/letIfTrue/letIfFalse 2023-01-22 23:01:06 +06:00
InsanusMokrassar 20799b9a3e add repeat on failure with callback 2023-01-22 22:43:26 +06:00
InsanusMokrassar ec3afc615c add serializable diff 2023-01-20 13:29:45 +06:00
InsanusMokrassar da692ccfc3 add ifTrue/ifFalse 2023-01-19 20:21:35 +06:00
InsanusMokrassar 53b89f3a18 start 0.16.7 2023-01-19 20:19:21 +06:00
InsanusMokrassar 58cded28d3 Merge pull request #217 from InsanusMokrassar/0.16.6
0.16.6
2023-01-18 22:28:44 +06:00
InsanusMokrassar 592c5f3732 Column extensions eqOrIsNull and neqOrIsNotNull 2023-01-14 20:17:57 +06:00
InsanusMokrassar f44a78a5f5 small improvement of openBaseWebSocketFlow 2023-01-14 15:20:52 +06:00
InsanusMokrassar e0bdd5dfdc improvements in microutils 2023-01-14 13:22:07 +06:00
InsanusMokrassar 99c0f06b72 repos update 2023-01-14 13:15:59 +06:00
InsanusMokrassar 66fc6df3d7 Improvements in 'StartLauncherPlugin#start' methods 2023-01-08 13:52:53 +06:00
InsanusMokrassar a36425a905 start 0.16.6 2023-01-08 13:51:34 +06:00
InsanusMokrassar d920fee6d4 Merge pull request #216 from InsanusMokrassar/0.16.5
0.16.5
2023-01-04 20:21:15 +06:00
InsanusMokrassar 23590be5de Update CHANGELOG.md 2023-01-04 20:20:25 +06:00
InsanusMokrassar 94acc3c93b Update libs.versions.toml 2023-01-04 08:56:46 +06:00
InsanusMokrassar 5616326a3b start 0.16.5 2023-01-04 08:50:19 +06:00
InsanusMokrassar 7601860c5c Merge pull request #214 from InsanusMokrassar/0.16.4
0.16.4
2022-12-27 19:03:35 +06:00
InsanusMokrassar 8b43d785cc update changelog 2022-12-27 18:51:01 +06:00
InsanusMokrassar b62d3a0b7d launchInCurrentThread 2022-12-27 18:50:41 +06:00
InsanusMokrassar fad73c7213 start 0.16.4 2022-12-27 18:49:04 +06:00
InsanusMokrassar 2403c7c2b0 Merge pull request #213 from InsanusMokrassar/0.16.3
0.16.3
2022-12-25 13:24:37 +06:00
InsanusMokrassar fa090bf920 Update StartLauncherPlugin.kt 2022-12-25 10:29:16 +06:00
InsanusMokrassar a83ee86340 improvements in startup launcher 2022-12-25 10:26:44 +06:00
InsanusMokrassar 204955bcce add template for startup and readme 2022-12-19 17:51:05 +06:00
InsanusMokrassar ee56e9543a start 0.16.3 2022-12-17 17:27:15 +06:00
InsanusMokrassar 96fdff6ffd Merge pull request #212 from InsanusMokrassar/0.16.2
0.16.2
2022-12-16 13:59:47 +06:00
InsanusMokrassar 58b007cbb3 fill changelog 2022-12-16 13:59:24 +06:00
InsanusMokrassar 4f0c139889 remove redundant stop koin expect-actual 2022-12-16 11:58:18 +06:00
InsanusMokrassar c584c24fce fixes and improvements 2022-12-15 15:07:10 +06:00
InsanusMokrassar 85e5cee154 replace createStartupPluginAndRegister 2022-12-15 14:59:07 +06:00
InsanusMokrassar 5af91981f1 upgrades and filling of README 2022-12-15 10:26:31 +06:00
InsanusMokrassar 2fe4f08059 update compose 2022-12-15 08:51:53 +06:00
InsanusMokrassar 83796f345a start fix startup 2022-12-14 22:26:23 +06:00
InsanusMokrassar c1e21364a6 start 0.16.2 2022-12-14 21:40:18 +06:00
InsanusMokrassar 067d9d0d3b Merge pull request #211 from InsanusMokrassar/0.16.1
0.16.1
2022-12-09 19:58:54 +06:00
InsanusMokrassar 03f527d83e Update CHANGELOG.md 2022-12-09 19:46:58 +06:00
InsanusMokrassar ced05a4586 improve default runCatchingSafely/safelyWithResult and add suspend variances of safe/unsafe SafeWrapper interface 2022-12-09 12:14:24 +06:00
InsanusMokrassar 43fe06206a add safe wrapper 2022-12-09 11:09:32 +06:00
InsanusMokrassar 023657558e start 0.16.1 2022-12-09 10:52:53 +06:00
InsanusMokrassar 9b0b726c80 Merge pull request #210 from InsanusMokrassar/0.16.0
0.16.0
2022-12-08 09:29:12 +06:00
InsanusMokrassar 4ee67321c4 fill changelog and update android dependencies 2022-12-08 09:26:59 +06:00
InsanusMokrassar 59f1f2e59b Update libs.versions.toml 2022-12-08 08:53:37 +06:00
InsanusMokrassar 0766d48b7c Update libs.versions.toml 2022-12-08 07:56:04 +06:00
InsanusMokrassar e18903b9e9 Update gradle.properties 2022-12-08 07:54:53 +06:00
InsanusMokrassar d0eecdead2 Update gradle.properties 2022-12-08 07:53:59 +06:00
InsanusMokrassar cc4a83a033 Update gradle.properties 2022-12-08 07:53:08 +06:00
InsanusMokrassar 1cf911bbde Update build.gradle 2022-12-07 19:40:30 +06:00
InsanusMokrassar 36d73d5023 Update StartupLaunchingTests.kt 2022-12-07 11:39:45 +06:00
InsanusMokrassar c395242e3e fixes in StartupLauncingTests 2022-12-07 10:50:51 +06:00
InsanusMokrassar cd9cd7cc5d Merge pull request #208 from InsanusMokrassar/0.15.1
0.15.1
2022-12-07 09:43:48 +06:00
InsanusMokrassar acbb8a0c07 complete prepare startup module 2022-12-06 13:15:08 +06:00
InsanusMokrassar b9d8528599 complete kdocs for the startup module 2022-12-06 13:14:05 +06:00
InsanusMokrassar 4971326eca add kdocs for the config of startup 2022-12-06 13:08:37 +06:00
InsanusMokrassar 09d1047260 add kdocs to the startup module 2022-12-06 13:06:21 +06:00
InsanusMokrassar 02dbd493c2 add tests and make several replacements/improvements 2022-12-06 12:38:24 +06:00
InsanusMokrassar b17931e7bd complete startup module 2022-12-05 22:31:15 +06:00
InsanusMokrassar 2a4570eafc Update ServerLauncher.kt 2022-12-05 21:52:36 +06:00
InsanusMokrassar c9514d3a6d Create DefaultJson.kt 2022-12-05 21:47:00 +06:00
InsanusMokrassar 072805efc7 Update ServerLauncher.kt 2022-12-05 21:45:30 +06:00
InsanusMokrassar 369ff26627 initialize startup module 2022-12-05 21:19:38 +06:00
InsanusMokrassar c5abbbbd2d start 0.15.1 2022-12-05 20:04:50 +06:00
InsanusMokrassar d974639f1e fixes in source roots of dokka 2022-12-05 07:47:35 +06:00
InsanusMokrassar 26efde316b remove redundant fix of android 32 dx files from worflows 2022-12-05 07:09:31 +06:00
InsanusMokrassar fafe50f80a suppress kdocs of uniupload in actual realizations 2022-12-05 07:00:31 +06:00
InsanusMokrassar 41504469db Revert "potential fix of dokka"
This reverts commit 31022733ac.
2022-12-05 06:57:29 +06:00
InsanusMokrassar 03b3ddd98b Revert "remove redundant dokka println"
This reverts commit b53cfd5504.
2022-12-05 06:57:27 +06:00
InsanusMokrassar 89d919f2be Merge pull request #207 from InsanusMokrassar/0.15.0
0.15.0
2022-12-04 19:26:14 +06:00
InsanusMokrassar b53cfd5504 remove redundant dokka println 2022-12-04 19:03:44 +06:00
InsanusMokrassar 31022733ac potential fix of dokka 2022-12-04 19:00:16 +06:00
InsanusMokrassar f9a8c39879 updates and fixes in mime types 2022-12-02 21:07:32 +06:00
InsanusMokrassar a812c2dd2f refresh language codes 2022-12-02 20:03:29 +06:00
InsanusMokrassar 217e977f0d ids in crud repos 2022-12-02 13:46:06 +06:00
InsanusMokrassar 04c301d1ac upgrade version up to 0.15.0 2022-12-02 13:31:11 +06:00
InsanusMokrassar 7f0c425389 start 0.14.5 2022-12-02 13:04:44 +06:00
InsanusMokrassar 1ede1c423b Merge pull request #206 from InsanusMokrassar/0.14.4
0.14.4
2022-11-27 17:34:07 +06:00
InsanusMokrassar 7cb064896a fill changelog 2022-11-27 14:44:03 +06:00
InsanusMokrassar 0c5e2862ca improvements in ktor client-server files handling 2022-11-27 14:38:35 +06:00
InsanusMokrassar 30d4507f54 hotfix in handleUniUpload 2022-11-26 12:12:01 +06:00
InsanusMokrassar b6c6d89455 Update gradle.properties 2022-11-25 10:16:31 +06:00
InsanusMokrassar 9d218ee534 Update UniloadMultipart.kt 2022-11-25 10:15:59 +06:00
InsanusMokrassar 11116d8cab Merge pull request #205 from InsanusMokrassar/0.14.3
0.14.3
2022-11-24 13:15:08 +06:00
InsanusMokrassar 58d754bbde handleUniUpload now is inline 2022-11-24 12:29:48 +06:00
InsanusMokrassar 8f25c123dd add download and downloadToTemporalFile extensions 2022-11-24 11:27:30 +06:00
InsanusMokrassar 76e214fc08 upfixes 2022-11-22 14:39:31 +06:00
InsanusMokrassar 2b5380f8d6 add server part of uniUpload, fill changelog 2022-11-22 13:38:36 +06:00
InsanusMokrassar 844a129094 complete universal uniupload 2022-11-22 12:57:43 +06:00
InsanusMokrassar a3090cca9d add uniupload for JVM 2022-11-22 10:36:15 +06:00
InsanusMokrassar b7b5159e9c Revert "Revert "start adding uniupload""
This reverts commit 0d1aae0ef7.
2022-11-22 09:51:10 +06:00
InsanusMokrassar 0f8bc2c950 fix in workflow build 2022-11-21 20:50:28 +06:00
InsanusMokrassar 69f5c49f45 small update of build workflows 2022-11-21 20:49:04 +06:00
InsanusMokrassar 9b308e6fb8 update workflow 2022-11-21 20:32:48 +06:00
InsanusMokrassar 3e3f91128b add dependency of common android repos onto jvm 2022-11-21 20:23:05 +06:00
InsanusMokrassar 0d1aae0ef7 Revert "start adding uniupload"
This reverts commit 4d022f0480.
2022-11-21 20:20:37 +06:00
InsanusMokrassar 4d022f0480 start adding uniupload 2022-11-21 13:39:11 +06:00
InsanusMokrassar 153e20d00e start 0.14.3 2022-11-21 12:49:04 +06:00
InsanusMokrassar a9a8171dd6 add gitea repo 2022-11-16 21:35:18 +06:00
InsanusMokrassar bf5c3b59b2 Merge pull request #204 from InsanusMokrassar/0.14.2
0.14.2
2022-11-15 10:02:11 +06:00
InsanusMokrassar 607c432bdb Update CHANGELOG.md 2022-11-15 10:01:58 +06:00
InsanusMokrassar ae5c010770 Update libs.versions.toml 2022-11-15 09:54:18 +06:00
InsanusMokrassar d5e432437f Update gradle.properties 2022-11-15 09:53:28 +06:00
InsanusMokrassar 9f99ebce01 Merge pull request #203 from InsanusMokrassar/0.14.1
0.14.1
2022-11-10 17:19:17 +06:00
InsanusMokrassar 64ee899b84 revert koin version change 2022-11-10 17:13:25 +06:00
InsanusMokrassar e0e0c1658b revert kotlin versin version change 2022-11-10 17:11:45 +06:00
InsanusMokrassar 2c586f667c update version 2022-11-10 17:09:12 +06:00
InsanusMokrassar 64164ef6c1 update dependencies 2022-11-10 17:06:20 +06:00
InsanusMokrassar 22343c0731 Merge pull request #202 from InsanusMokrassar/0.14.0
0.14.0
2022-11-08 14:20:12 +06:00
InsanusMokrassar f4ec1a4c60 add deprecations removing note 2022-11-08 14:19:48 +06:00
InsanusMokrassar c1c33cceb1 remove deprecations 2022-11-08 14:17:45 +06:00
InsanusMokrassar a3e975b2ba Update CHANGELOG.md 2022-11-08 14:13:36 +06:00
InsanusMokrassar 06e705a687 update klock 2022-11-08 13:12:39 +06:00
InsanusMokrassar d56eb6c867 fixes 2022-11-08 12:47:59 +06:00
InsanusMokrassar 9cbca864e3 Update CHANGELOG.md 2022-11-08 08:00:18 +06:00
InsanusMokrassar abb4378694 Update libs.versions.toml 2022-11-08 07:59:50 +06:00
InsanusMokrassar 0eb698d9a4 start 0.14.0 2022-11-08 07:57:31 +06:00
InsanusMokrassar 15ea9f2093 add documentation for KeyValue repo 2022-11-03 13:01:51 +06:00
InsanusMokrassar d47aca0923 Merge pull request #201 from InsanusMokrassar/0.13.2
0.13.2
2022-10-30 22:35:07 +06:00
InsanusMokrassar 1ac50e9959 update several dependencies and fill changelog 2022-10-30 21:45:19 +06:00
InsanusMokrassar 6adfbe3a96 Update exposed and revert several dependencies updates 2022-10-24 23:52:04 +06:00
InsanusMokrassar 59f36e62e9 update dependencies 2022-10-22 14:25:46 +06:00
InsanusMokrassar 54af116009 start 0.13.2 2022-10-22 14:14:12 +06:00
InsanusMokrassar 38fbec8e3b Merge pull request #200 from InsanusMokrassar/0.13.1
0.13.1
2022-10-17 15:42:13 +06:00
InsanusMokrassar babbfc55e4 update default of AbstractExposedWriteCRUDRepo 2022-10-17 15:31:27 +06:00
InsanusMokrassar 2511e18d69 AbstractExposedWriteCRUDRepo#createAndInsertId now is optional and returns nullable value 2022-10-17 14:42:53 +06:00
InsanusMokrassar 29658c70a0 start 0.13.1 2022-10-17 14:35:16 +06:00
InsanusMokrassar 96311ee43d Merge pull request #199 from InsanusMokrassar/0.13.0
0.13.0
2022-10-13 16:50:50 +06:00
InsanusMokrassar bd33b09052 A LOT OF KTOR METHODS RELATED TO UnifierRouter/UnifiedRequester HAVE BEEN REMOVED 2022-10-11 13:24:39 +06:00
InsanusMokrassar 8055003b47 deprecations removing 2022-10-11 12:58:29 +06:00
InsanusMokrassar 1257492f85 changes in insert/update exposed methods and rewriting of read keyvalue(s) exposed repos to be extending abstract key value(s) repos 2022-10-11 12:55:25 +06:00
InsanusMokrassar 1107b7f4ef start 0.13.0 2022-10-11 12:07:49 +06:00
InsanusMokrassar a1a1171240 start 0.12.18 2022-10-04 15:36:43 +06:00
InsanusMokrassar 46c02e5df1 Merge pull request #198 from InsanusMokrassar/0.12.17
0.12.17
2022-10-01 21:59:42 +06:00
InsanusMokrassar 2e9efc57de update dependencies 2022-10-01 21:56:47 +06:00
InsanusMokrassar acecadef17 start 0.12.17 2022-10-01 21:41:34 +06:00
InsanusMokrassar 19394b5e69 Merge pull request #197 from InsanusMokrassar/0.12.16
0.12.16
2022-09-26 02:16:17 +06:00
InsanusMokrassar de999e197f fix of setOnHierarchyChangeListenerRecursively 2022-09-26 01:10:10 +06:00
InsanusMokrassar 9d95687d3c FlowOnHierarchyChangeListener 2022-09-26 01:02:12 +06:00
InsanusMokrassar aa9dfb4ab8 start 0.12.16 2022-09-26 00:47:32 +06:00
InsanusMokrassar 9c5b44efb3 Merge pull request #196 from InsanusMokrassar/0.12.15
0.12.15
2022-09-24 01:04:01 +06:00
InsanusMokrassar ac587a67e6 findViewsByTag 2022-09-23 16:43:33 +06:00
InsanusMokrassar 59428140a8 applyDiff: Diff 2022-09-23 16:05:32 +06:00
InsanusMokrassar 60bdb59d71 fix version change 2022-09-23 12:41:57 +06:00
InsanusMokrassar be52871de8 flows extensions 2022-09-23 12:41:07 +06:00
InsanusMokrassar b7934cf357 flows extensions 2022-09-23 12:33:00 +06:00
InsanusMokrassar dbfbeef90a start 0.12.15 2022-09-23 12:12:15 +06:00
InsanusMokrassar 00943c9cdf add docs for Diff class 2022-09-23 12:10:24 +06:00
InsanusMokrassar 8745c6a16a Merge pull request #195 from InsanusMokrassar/0.12.14
0.12.14
2022-09-23 00:19:12 +06:00
InsanusMokrassar 433ba4b58f argumentOrNull/argumentOrThrow 2022-09-22 23:39:08 +06:00
InsanusMokrassar d40376e524 Update CHANGELOG.md 2022-09-22 21:51:40 +06:00
InsanusMokrassar a2982f88f5 Update libs.versions.toml 2022-09-21 21:17:34 +06:00
InsanusMokrassar 1642f7abd9 improvements in AwaitFirst 2022-09-21 18:00:39 +06:00
InsanusMokrassar a10d2184ff start 0.12.14 2022-09-21 17:53:57 +06:00
InsanusMokrassar 522435f096 Merge pull request #194 from InsanusMokrassar/0.12.13
make actor not async
2022-09-15 01:43:21 +06:00
InsanusMokrassar 79b30290c0 fix build 2022-09-15 01:19:05 +06:00
InsanusMokrassar f8b8626859 make actor not async 2022-09-15 01:11:20 +06:00
InsanusMokrassar b061b85a08 Merge pull request #193 from InsanusMokrassar/0.12.13
0.12.13
2022-09-14 23:27:59 +06:00
InsanusMokrassar 3870db1c88 fix of #155 and build 2022-09-14 22:47:57 +06:00
InsanusMokrassar 1be1070eb4 fix of #160 2022-09-14 22:32:00 +06:00
InsanusMokrassar 2696e663cf add RandomQualifier 2022-09-14 22:21:30 +06:00
InsanusMokrassar 1e1f7df86d create koin module 2022-09-14 22:14:20 +06:00
InsanusMokrassar 1d8ded8fd3 start 0.12.13 2022-09-14 21:42:29 +06:00
InsanusMokrassar 197825123a Merge pull request #192 from InsanusMokrassar/0.12.12
0.12.12
2022-09-13 22:08:52 +06:00
InsanusMokrassar 422b2e6db1 improve SkeletonAnimation 2022-09-13 22:01:02 +06:00
InsanusMokrassar 1973e0b5bf SkeletonAnimation 2022-09-13 01:30:36 +06:00
InsanusMokrassar 8258cf93a9 start 0.12.12 2022-09-12 21:03:07 +06:00
InsanusMokrassar 1d49bd5947 Merge pull request #191 from InsanusMokrassar/0.12.11
0.12.11
2022-09-08 23:46:55 +06:00
InsanusMokrassar 44317d1519 start 0.12.11 and add values in key value cache repo 2022-09-08 22:38:24 +06:00
InsanusMokrassar 48e08fcc69 Merge pull request #190 from InsanusMokrassar/0.12.10
0.12.10
2022-09-07 23:54:33 +06:00
InsanusMokrassar 1a3ce6e623 hotfix 2022-09-07 23:47:13 +06:00
InsanusMokrassar fb122f3e70 0.12.10 2022-09-07 23:45:59 +06:00
InsanusMokrassar 7cca6039cc Merge pull request #189 from InsanusMokrassar/0.12.9
fix of cache filling without clearing of values
2022-09-07 22:16:50 +06:00
InsanusMokrassar 118e3dba39 fix of cache filling without clearing of values 2022-09-07 21:56:41 +06:00
InsanusMokrassar 87070710fa Merge pull request #188 from InsanusMokrassar/0.12.9
update korlibs
2022-09-07 21:14:26 +06:00
InsanusMokrassar 38499c3d4a update korlibs 2022-09-07 21:09:57 +06:00
InsanusMokrassar 5d754d968b Merge pull request #187 from InsanusMokrassar/0.12.9
0.12.9
2022-09-07 20:26:46 +06:00
InsanusMokrassar d543d436bc Fixes in key values cache 2022-09-07 20:01:13 +06:00
InsanusMokrassar 12b54f99af update gradle wrapper 2022-09-07 19:49:29 +06:00
InsanusMokrassar 2a95d7e643 start 0.12.9 2022-09-07 19:48:59 +06:00
InsanusMokrassar e3d3cacfa4 Merge pull request #186 from InsanusMokrassar/0.12.8
0.12.8
2022-09-07 00:57:17 +06:00
InsanusMokrassar 4b13491a0e Update CHANGELOG.md 2022-09-07 00:56:48 +06:00
InsanusMokrassar 85d516d1e9 Update libs.versions.toml 2022-09-06 23:44:02 +06:00
InsanusMokrassar ac58b6a7e3 bodyOrNull/respondOrNoContent 2022-09-06 21:16:41 +06:00
InsanusMokrassar 2cc6126765 start 0.12.8 2022-09-06 21:15:01 +06:00
InsanusMokrassar f94b085850 Merge pull request #185 from InsanusMokrassar/0.12.7
0.12.7
2022-09-05 18:35:56 +06:00
InsanusMokrassar c9822a491b WriteCRUDCacheRepo subscribtions and changeResultsUnchecked(Pagination) 2022-09-05 15:07:33 +06:00
InsanusMokrassar 23b2d60295 start 0.12.7 2022-09-05 14:20:58 +06:00
InsanusMokrassar f4bc9eed39 Merge pull request #184 from InsanusMokrassar/0.12.6
0.12.6
2022-08-31 04:10:33 +06:00
InsanusMokrassar e310f188b0 fixed absence of image/* in known mime types 2022-08-31 03:01:06 +06:00
InsanusMokrassar 6c1571188c start 0.12.6 2022-08-31 02:59:29 +06:00
InsanusMokrassar 945d2fa284 Merge pull request #183 from InsanusMokrassar/0.12.5
0.12.5
2022-08-30 17:38:57 +06:00
InsanusMokrassar 020095f1ff Update CHANGELOG.md 2022-08-30 17:37:59 +06:00
InsanusMokrassar b165a76e62 fixes in selectPaginated 2022-08-30 15:20:45 +06:00
InsanusMokrassar 03c8830672 start 0.12.5 2022-08-30 15:17:09 +06:00
InsanusMokrassar 38448da89b Merge pull request #182 from InsanusMokrassar/0.12.4
0.12.4
2022-08-23 13:02:19 +06:00
InsanusMokrassar 2ade5aff91 update dependencies 2022-08-23 12:57:16 +06:00
InsanusMokrassar 29bf6e80ec start 0.12.4 2022-08-23 12:55:52 +06:00
InsanusMokrassar 027e927e1b Merge pull request #181 from InsanusMokrassar/0.12.3
0.12.3
2022-08-22 01:44:17 +06:00
InsanusMokrassar fa061f88e2 refactor 2022-08-22 01:42:04 +06:00
InsanusMokrassar 1d01b65b5f fix 2022-08-22 01:26:28 +06:00
InsanusMokrassar 2c2b364167 add default unoptimized realization of selectByIds 2022-08-22 01:25:07 +06:00
InsanusMokrassar 7c5fc9bf7c selectPaginated and abstract exposed keyvalue(s) repos 2022-08-22 01:11:09 +06:00
InsanusMokrassar 193d22ff20 start 0.12.3 2022-08-22 00:02:34 +06:00
InsanusMokrassar 0f3b553ba2 Merge pull request #180 from InsanusMokrassar/0.12.2
0.12.2
2022-08-19 16:25:24 +06:00
InsanusMokrassar 4eb1013446 Update CHANGELOG.md 2022-08-19 15:54:46 +06:00
InsanusMokrassar be1b2563ec Update libs.versions.toml 2022-08-19 15:54:30 +06:00
InsanusMokrassar 1c9c2f1e70 Update libs.versions.toml 2022-08-19 02:00:29 +06:00
InsanusMokrassar 52dfded741 start 0.12.2 2022-08-19 01:59:09 +06:00
InsanusMokrassar 0a115e5cf4 Merge pull request #179 from InsanusMokrassar/0.12.1
0.12.1
2022-08-11 23:50:12 +06:00
InsanusMokrassar c349af999b Update CHANGELOG.md 2022-08-11 23:36:01 +06:00
InsanusMokrassar 9a5709a34d Update libs.versions.toml 2022-08-11 22:52:34 +06:00
InsanusMokrassar 28d311160b start 0.12.1 2022-08-11 22:47:56 +06:00
InsanusMokrassar 1ca7081a40 Merge pull request #176 from InsanusMokrassar/0.12.0
0.12.0
2022-08-04 13:55:30 +06:00
274 changed files with 9811 additions and 3924 deletions

No files matched your search

+5 -9
View File
@@ -9,9 +9,6 @@ jobs:
- uses: actions/setup-java@v1
with:
java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
- name: Rewrite version
run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
@@ -20,9 +17,8 @@ jobs:
mv gradle.properties.tmp gradle.properties
- name: Build
run: ./gradlew build
# - name: Publish
# continue-on-error: true
# run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository
# env:
# GITHUBPACKAGES_USER: ${{ github.actor }}
# GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
- name: Publish
continue-on-error: true
run: ./gradlew publishAllPublicationsToGiteaRepository
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
-3
View File
@@ -11,9 +11,6 @@ jobs:
- uses: actions/setup-java@v1
with:
java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
- name: Build
run: ./gradlew build && ./gradlew dokkaHtml
- name: Publish KDocs
-8
View File
@@ -1,8 +0,0 @@
job("Build and run tests") {
container(displayName = "Run gradle build", image = "openjdk:11") {
kotlinScript { api ->
// here can be your complex logic
api.gradlew("build")
}
}
}
+440 -1
View File
@@ -1,6 +1,445 @@
# Changelog
## 0.12.0-beta
## 0.18.0
**ALL PREVIOUSLY DEPRECATED FUNCTIONALITY HAVE BEEN REMOVED**
* `Versions`:
* `Android Fragments`: `1.5.6` -> `1.5.7`
* `Ktor`:
* `Server`:
* Now it is possible to take query parameters as list
* `Repos`:
* `Common`:
* New `WriteKeyValuesRepo.removeWithValue`
* `Cache`:
* Rename full caching factories functions to `fullyCached`
## 0.17.8
* `Versions`:
* `Ktor`: `2.2.4` -> `2.3.0`
## 0.17.7
* `Versions`:
* `Android CoreKtx`: `1.9.0` -> `1.10.0`
* `Startup`:
* Add support of `linuxX64` and `mingwX64` platforms
## 0.17.6
* `Versions`:
* `Kotlin`: `1.8.10` -> `1.8.20`
* `KSLog`: `1.0.0` -> `1.1.1`
* `Compose`: `1.3.1` -> `1.4.0`
* `Koin`: `3.3.2` -> `3.4.0`
* `RecyclerView`: `1.2.1` -> `1.3.0`
* `Fragment`: `1.5.5` -> `1.5.6`
* Experimentally (`!!!`) add `linuxX64` and `mingwX64` targets
## 0.17.5
* `Common`:
* Conversations of number primitives with bounds care
* `Repos`:
* `Common`:
* By default, `getAll` for repos will take all the size of repo as page size
* New extension for all built-in repos `maxPagePagination`
* All the repos got `getAll` functions
## 0.17.4
* `Serialization`:
* `Mapper`:
* Module inited
* `Versions`:
* `Compose`: `1.3.1-rc02` -> `1.3.1`
## 0.17.3
* `Common`:
* Add `fixed` extensions for `Float` and `Double`
* New function `emptyDiff`
* Now you may pass custom `comparisonFun` to all `diff` functions
## 0.17.2
* `FSM`:
* `DefaultStatesManager.onUpdateContextsConflictResolver` and `DefaultStatesManager.onStartContextsConflictResolver` now return `false` by default
## 0.17.1
* **Hotfix** for absence of jvm dependencies in android modules
* `Versions`:
* `Ktor`: `2.2.3` -> `2.2.4`
## 0.17.0
* `Versions`:
* `Kotlin`: `1.7.20` -> `1.8.10`
* `Serialization`: `1.4.1` -> `1.5.0`
* `KSLog`: `0.5.4` -> `1.0.0`
* `AppCompat`: `1.6.0` -> `1.6.1`
## 0.16.13
* `Repos`:
* `Generator`:
* Module has been created
## 0.16.12
* `Repos`:
* `Exposed`:
* `CommonExposedRepo.selectByIds` uses `foldRight` by default instead of raw foreach
* `Koin`:
* `Generator`:
* Module has been created
## 0.16.11
* `LanguageCodes`:
* In android and JVM targets now available `toJavaLocale` and from Java `Locale` conversations from/to
`IetfLanguageCode`
## 0.16.10
* `Repos`:
* `Cache`:
* New transformer type: `ReadCRUDFromKeyValueRepo`
* New transformer type: `ReadKeyValueFromCRUDRepo`
* `Pagination`:
* New `paginate` extensions with `reversed` support for `List`/`Set`
## 0.16.9
* `Versions`:
* `Koin`: `3.2.2` -> `3.3.2`
* `AppCompat`: `1.5.1` -> `1.6.0`
* `Ktor`:
* `Client`
* `HttpResponse.bodyOrNull` now retrieve callback to check if body should be received or null
* New extension `HttpResponse.bodyOrNullOnNoContent`
## 0.16.8
* `Versions`:
* `Ktor`: `2.2.2` -> `2.2.3`
* `Ktor`:
* `Client`
* Fixes in `HttpClient.uniUpload`
* `Server`
* Fixes in `PartData.FileItem.download`
* `Repos`:
* `Cache`:
* New type of caches: `FallbackCacheRepo`
* Fixes in `Write*` variants of cached repos
* New type `ActionWrapper`
* New `AutoRecache*` classes for all types of repos as `FallbackCacheRepo`s
* `Common`:
* New transformations for key-value and key-values vice-verse
* Fixes in `FileReadKeyValueRepo`
## 0.16.7
* `Common`:
* New extensions `ifTrue`/`ifFalse`/`alsoIfTrue`/`alsoIfFalse`/`letIfTrue`/`letIfFalse`
* `Diff` now is serializable
* Add `IndexedValue` serializer
* `repeatOnFailure` extending: now you may pass any lambda to check if continue to try/do something
* `Compose`:
* New extension `MutableState.asState`
* `Coroutines`:
* `Compose`:
* All the `Flow` conversations to compose `State`/`MutableState`/`SnapshotStateList`/`List` got several new
parameters
* `Flow.toMutableState` now is deprecated in favor to `asMutableComposeState`
* `Repos`:
* `Cache`:
* New type `FullCacheRepo`
* New type `CommonCacheRepo`
* `CacheRepo` got `invalidate` method. It will fully reload `FullCacheRepo` and just clear `CommonCacheRepo`
* New extensions `KVCache.actualizeAll`
## 0.16.6
* `Startup`:
* `Launcher`:
* Improvements in `StartLauncherPlugin#start` methods
* Add opportunity to pass second argument on `JVM` platform as log level
* `Repos`:
* `Ktor`:
* `Client`:
* All clients repos got opportunity to customize their flows
* `Exposed`:
* Extensions `eqOrIsNull` and `neqOrIsNotNull` for `Column`
## 0.16.5
* `Versions`:
* `Ktor`: `2.2.1` -> `2.2.2`
## 0.16.4
* `Coroutines`:
* Create `launchInCurrentThread`
## 0.16.3
* `Startup`:
* `Launcher`:
* All starting API have been moved into `StartLauncherPlugin` and do not require serialize/deserialize cycle for now
## 0.16.2
* `Versions`:
* `Compose`: `1.2.1` -> `1.2.2`
* `Startup`:
* Module become available on `JS` target
## 0.16.1
* `Coroutines`:
* New `runCatchingSafely`/`safelyWithResult` with receivers
* `SafeWrapper`:
* Module inited
## 0.16.0
* `Versions`:
* `Ktor`: `2.1.3` -> `2.2.1`
* `Android Fragment`: `1.5.3` -> `1.5.5`
## 0.15.1
* `Startup`:
* Inited :)
* `Plugin`:
* Inited :)
* `Launcher`:
* Inited :)
## 0.15.0
* `Repos`:
* `CRUD`:
* `Common`:
* New method `ReadCRUDRepo#getIdsByPagination`
* `Android`:
* `AbstractAndroidCRUDRepo` got new abstract method `toId`
* `Exposed`:
* `CommonExposedRepo` new abstract property `asId`
* `Ktor`:
* `Client`:
* `KtorReadCRUDRepoClient` now requires `paginationIdType`
* `LanguageCodes`:
* Updates and fixes in generation
* `MimeTypes`:
* Updates and fixes in generation
## 0.14.4
* `Common`:
* `JVM`:
* New extension `downloadToTempFile`
* `Ktor`:
* `Server`:
* Small fix in `handleUniUpload`
* `ApplicationCall#uniloadMultipartFile` now uses `uniloadMultipart`
* `Common`:
* New extension `downloadToTempFile`
* `Client`:
* New extensions on top of `uniUpload`
## 0.14.3
* `Common`:
* New type `Progress`
* `Ktor`:
* `Client`:
* New universal `uniUpload` extension for `HttpClient`
* `Server`:
* New universal `handleUniUpload` extension for `ApplicationCall`
* Add extensions `download` and `downloadToTemporalFile`
## 0.14.2
* `Versions`:
* `Exposed`: `0.40.1` -> `0.41.1`
## 0.14.1
* `Versions`:
* `Klock`: `3.3.1` -> `3.4.0`
* `UUID`: `0.5.0` -> `0.6.0`
## 0.14.0
**ALL DEPRECATIONS HAVE BEEN REMOVED**
* `Versions`:
* `Kotlin`: `1.7.10` -> `1.7.20`
* `Klock`: `3.3.0` -> `3.3.1`
* `Compose`: `1.2.0` -> `1.2.1`
* `Exposed`: `0.39.2` -> `0.40.1`
## 0.13.2
* `Versions`:
* `Klock`: `3.1.0` -> `3.3.0`
* `Ktor`: `2.1.2` -> `2.1.3`
## 0.13.1
* `Repos`:
* `Exposed`:
* `AbstractExposedWriteCRUDRepo#createAndInsertId` now is optional and returns nullable value
## 0.13.0
**ALL DEPRECATIONS HAVE BEEN REMOVED**
**A LOT OF KTOR METHODS RELATED TO UnifierRouter/UnifiedRequester HAVE BEEN REMOVED**
* `Repos`:
* `Exposed`:
* `AbstractExposedWriteCRUDRepo` got two new methods: `update` with `it` as `UpdateBuilder<Int>` and `createAndInsertId`
* Old `update` method has been deprecated and not recommended to override anymore in realizations
* Old `insert` method now is `open` instead of `abstract` and can be omitted
* `AbstractExposedKeyValueRepo` got two new methods: `update` with `it` as `UpdateBuilder<Int>` and `insertKey`
* Old `update` method has been deprecated and not recommended to override anymore
* Old `insert` method now is `open` instead of `abstract` and can be omitted in realizations
## 0.12.17
* `Versions`:
* `JB Compose`: `1.2.0-alpha01-dev774` -> `1.2.0-beta02`
* `Ktor`: `2.1.1` -> `2.1.2`
* `Koin`: `3.2.0` -> `3.2.2`
## 0.12.16
* `Coroutines`:
* `Android`:
* Add class `FlowOnHierarchyChangeListener`
* Add `ViewGroup#setOnHierarchyChangeListenerRecursively(OnHierarchyChangeListener)`
## 0.12.15
* `Common`:
* `applyDiff` will return `Diff` object since this release
* `Android`:
* New functions/extensions `findViewsByTag` and `findViewsByTagInActivity`
* `Coroutines`:
* Add `Flow` extensions `flatMap`, `flatMapNotNull` and `flatten`
* Add `Flow` extensions `takeNotNull` and `filterNotNull`
## 0.12.14
* `Versions`:
* `Android CoreKTX`: `1.8.0` -> `1.9.0`
* `Android AppCompat`: `1.4.2` -> `1.5.1`
* Android Compile SDK: 32 -> 33
* Android Build Tools: 32.0.0 -> 33.0.0
* `Common`:
* `Android`:
* Add `argumentOrNull`/`argumentOrThrow` delegates for fragments
* `Coroutines`:
* Rewrite `awaitFirstWithDeferred` onto `CompletableDeferred` instead of coroutines suspending
## 0.12.13
* `Coroutines`:
* Add opportunity to use markers in actors (solution of [#160](https://github.com/InsanusMokrassar/MicroUtils/issues/160))
* `Koin`:
* Module inited :)
* `Repos`:
* `Android`:
* Add typealias `KeyValueSPRepo` and opportunity to create shared preferences `KeyValue` repo with `KeyValueStore(...)` (fix of [#155](https://github.com/InsanusMokrassar/MicroUtils/issues/155))
## 0.12.12
* `Common`:
* `Compose`:
* `JS`:
* Add `SkeletonAnimation` stylesheet
## 0.12.11
* `Repos`:
* `Cache`:
* Override `KeyValue` cache method `values`
## 0.12.10
* `Repos`:
* `Cache`:
* Hotfix in key values `get`
## 0.12.9
* `Versions`:
* `Klock`: `3.0.0` -> `3.1.0`
* `Repos`:
* `Cache`:
* Fixes in key values cache
## 0.12.8
* `Versions`:
* `Ktor`: `2.1.0` -> `2.1.1`
* `Compose`: `1.2.0-alpha01-dev764` -> `1.2.0-alpha01-dev774`
* `Ktor`:
* `Client`:
* New extension `HttpClient#bodyOrNull` which returns `null` in case when server responded with `No Content` (204)
* `Server`:
* New extension `ApplicationCall#respondOrNoContent` which responds `No Content` (204) when passed data is null
## 0.12.7
* `Repos`:
* `Cache`:
* Force `WriteCRUDCacheRepo` to subscribe on new and updated objects of parent repo
* `Pagination`:
* New function `changeResultsUnchecked(Pagination)`
## 0.12.6
* `MimeeTypes>`:
* Fixed absence of `image/*` in known mime types
## 0.12.5
* `Repos`:
* `Exposed`:
* Fixes in `paginate` extensions
## 0.12.4
* `Versions`:
* `Kotlin`: `1.7.0` -> `1.7.10`
* `Compose`: `1.2.0-alpha01-dev755` -> `1.2.0-alpha01-dev764`
## 0.12.3
* `Repos`:
* `Exposed`:
* Add abstract exposed variants of `KeyValue` and `KeyValues` repos
* Add new extension `Query#selectPaginated`
## 0.12.2
* `Versions`:
* `Serialization`: `1.4.0-RC` -> `1.4.0`
* `Compose`: `1.2.0-alpha01-dev753` -> `1.2.0-alpha01-dev755`
## 0.12.1
* `Versions`:
* `Ktor`: `2.0.3` -> `2.1.0`
## 0.12.0
**OLD DEPRECATIONS HAVE BEEN REMOVED**
+2
View File
@@ -9,6 +9,7 @@ buildscript {
dependencies {
classpath libs.buildscript.kt.gradle
classpath libs.buildscript.kt.serialization
classpath libs.buildscript.kt.ksp
classpath libs.buildscript.jb.dokka
classpath libs.buildscript.gh.release
classpath libs.buildscript.android.gradle
@@ -22,6 +23,7 @@ allprojects {
mavenCentral()
google()
maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" }
maven { url "https://git.inmo.dev/api/packages/InsanusMokrassar/maven" }
}
// temporal crutch until legacy tests will be stabled or legacy target will be removed
+13
View File
@@ -16,6 +16,19 @@ kotlin {
androidMain {
dependencies {
api project(":micro_utils.coroutines")
api libs.android.fragment
}
dependsOn jvmMain
}
linuxX64Main {
dependencies {
api libs.okio
}
}
mingwX64Main {
dependencies {
api libs.okio
}
}
}
@@ -0,0 +1,10 @@
package dev.inmo.micro_utils.common.compose
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
/**
* Converts current [MutableState] to immutable [State] using [derivedStateOf]
*/
fun <T> MutableState<T>.asState(): State<T> = derivedStateOf { this.value }
@@ -0,0 +1,43 @@
package dev.inmo.micro_utils.common.compose
import org.jetbrains.compose.web.css.*
object SkeletonAnimation : StyleSheet() {
val skeletonKeyFrames: CSSNamedKeyframes by keyframes {
to { backgroundPosition("-20% 0") }
}
fun CSSBuilder.includeSkeletonStyle(
duration: CSSSizeValue<out CSSUnitTime> = 2.s,
timingFunction: AnimationTimingFunction = AnimationTimingFunction.EaseInOut,
iterationCount: Int? = null,
direction: AnimationDirection = AnimationDirection.Normal,
keyFrames: CSSNamedKeyframes = skeletonKeyFrames,
hideChildren: Boolean = true,
hideText: Boolean = hideChildren
) {
backgroundImage("linear-gradient(110deg, rgb(236, 236, 236) 40%, rgb(245, 245, 245) 50%, rgb(236, 236, 236) 65%)")
backgroundSize("200% 100%")
backgroundPosition("180% 0")
animation(keyFrames) {
duration(duration)
timingFunction(timingFunction)
iterationCount(iterationCount)
direction(direction)
}
if (hideText) {
property("color", "${Color.transparent} !important")
}
if (hideChildren) {
child(self, universal) style {
property("visibility", "hidden")
}
}
}
val skeleton by style {
includeSkeletonStyle()
}
}
@@ -2,6 +2,8 @@
package dev.inmo.micro_utils.common
import kotlinx.serialization.Serializable
private inline fun <T> getObject(
additional: MutableList<T>,
iterator: Iterator<T>
@@ -14,16 +16,29 @@ private inline fun <T> getObject(
/**
* Diff object which contains information about differences between two [Iterable]s
*
* See tests for more info
*
* @param removed The objects which has been presented in the old collection but absent in new one. Index here is the index in the old collection
* @param added The object which appear in new collection only. Indexes here show the index in the new collection
* @param replaced Pair of old-new changes. First object has been presented in the old collection on its
* [IndexedValue.index] place, the second one is the object in new collection. Both have indexes due to the fact that in
* case when some value has been replaced after adds or removes in original collection the object index will be changed
*
* @see calculateDiff
*/
@Serializable
data class Diff<T> internal constructor(
val removed: List<IndexedValue<T>>,
val removed: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>,
/**
* Old-New values pairs
*/
val replaced: List<Pair<IndexedValue<T>, IndexedValue<T>>>,
val added: List<IndexedValue<T>>
)
val replaced: List<Pair<@Serializable(IndexedValueSerializer::class) IndexedValue<T>, @Serializable(IndexedValueSerializer::class) IndexedValue<T>>>,
val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>
) {
fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty()
}
fun <T> emptyDiff(): Diff<T> = Diff(emptyList(), emptyList(), emptyList())
private inline fun <T> performChanges(
potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
@@ -32,14 +47,14 @@ private inline fun <T> performChanges(
changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
removedList: MutableList<IndexedValue<T>>,
addedList: MutableList<IndexedValue<T>>,
strictComparison: Boolean
comparisonFun: (T?, T?) -> Boolean
) {
var i = -1
val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return
for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) {
i++
val oldOneEqualToNewObject = old ?.value === newObject ?.value || (old ?.value == newObject ?.value && !strictComparison)
val newOneEqualToOldObject = new ?.value === oldObject ?.value || (new ?.value == oldObject ?.value && !strictComparison)
val oldOneEqualToNewObject = comparisonFun(old ?.value, newObject ?.value)
val newOneEqualToOldObject = comparisonFun(new ?.value, oldObject ?.value)
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
changedList.addAll(
potentialChanges.take(i).mapNotNull {
@@ -93,7 +108,7 @@ private inline fun <T> performChanges(
*/
fun <T> Iterable<T>.calculateDiff(
other: Iterable<T>,
strictComparison: Boolean = false
comparisonFun: (T?, T?) -> Boolean
): Diff<T> {
var i = -1
var j = -1
@@ -121,7 +136,7 @@ fun <T> Iterable<T>.calculateDiff(
}
when {
oldObject === newObject || (oldObject == newObject && !strictComparison) -> {
comparisonFun(oldObject, newObject) -> {
changedObjects.addAll(potentiallyChangedObjects.map {
@Suppress("UNCHECKED_CAST")
it as Pair<IndexedValue<T>, IndexedValue<T>>
@@ -132,23 +147,49 @@ fun <T> Iterable<T>.calculateDiff(
potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) })
val previousOldsAdditionsSize = additionalInOld.size
val previousNewsAdditionsSize = additionalInNew.size
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
i -= (additionalInOld.size - previousOldsAdditionsSize)
j -= (additionalInNew.size - previousNewsAdditionsSize)
}
}
}
potentiallyChangedObjects.add(null to null)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList())
}
/**
* Calculating [Diff] object
*
* @param strictComparison If this parameter set to true, objects which are not equal by links will be used as different
* objects. For example, in case of two "Example" string they will be equal by value, but CAN be different by links
*/
fun <T> Iterable<T>.calculateDiff(
other: Iterable<T>,
strictComparison: Boolean = false
): Diff<T> = calculateDiff(
other,
comparisonFun = if (strictComparison) {
{ t1, t2 ->
t1 === t2
}
} else {
{ t1, t2 ->
t1 === t2 || t1 == t2 // small optimization for cases when t1 and t2 are the same - comparison will be faster potentially
}
}
)
inline fun <T> Iterable<T>.diff(
other: Iterable<T>,
strictComparison: Boolean = false
): Diff<T> = calculateDiff(other, strictComparison)
inline fun <T> Iterable<T>.diff(
other: Iterable<T>,
noinline comparisonFun: (T?, T?) -> Boolean
): Diff<T> = calculateDiff(other, comparisonFun)
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, strictComparison = false)
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)
/**
@@ -165,7 +206,26 @@ inline fun <T> Iterable<T>.calculateStrictDiff(
fun <T> MutableList<T>.applyDiff(
source: Iterable<T>,
strictComparison: Boolean = false
) = calculateDiff(source, strictComparison).let {
): Diff<T> = calculateDiff(source, strictComparison).also {
for (i in it.removed.indices.sortedDescending()) {
removeAt(it.removed[i].index)
}
it.added.forEach { (i, t) ->
add(i, t)
}
it.replaced.forEach { (_, new) ->
set(new.index, new.value)
}
}
/**
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
* mutable list
*/
fun <T> MutableList<T>.applyDiff(
source: Iterable<T>,
comparisonFun: (T?, T?) -> Boolean
): Diff<T> = calculateDiff(source, comparisonFun).also {
for (i in it.removed.indices.sortedDescending()) {
removeAt(it.removed[i].index)
}
@@ -0,0 +1,43 @@
package dev.inmo.micro_utils.common
inline fun <T> Boolean.letIfTrue(block: () -> T): T? {
return if (this) {
block()
} else {
null
}
}
inline fun <T> Boolean.letIfFalse(block: () -> T): T? {
return if (this) {
null
} else {
block()
}
}
inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean {
letIfTrue(block)
return this
}
inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean {
letIfFalse(block)
return this
}
inline fun <T> Boolean.ifTrue(block: () -> T): T? {
return if (this) {
block()
} else {
null
}
}
inline fun <T> Boolean.ifFalse(block: () -> T): T? {
return if (this) {
null
} else {
block()
}
}
@@ -0,0 +1,30 @@
package dev.inmo.micro_utils.common
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
class IndexedValueSerializer<T>(private val subSerializer: KSerializer<T>) : KSerializer<IndexedValue<T>> {
private val originalSerializer = PairSerializer(Int.serializer(), subSerializer)
override val descriptor: SerialDescriptor
get() = originalSerializer.descriptor
override fun deserialize(decoder: Decoder): IndexedValue<T> {
val pair = originalSerializer.deserialize(decoder)
return IndexedValue(
pair.first,
pair.second
)
}
override fun serialize(encoder: Encoder, value: IndexedValue<T>) {
originalSerializer.serialize(
encoder,
Pair(value.index, value.value)
)
}
}
@@ -0,0 +1,28 @@
package dev.inmo.micro_utils.common
/**
* Convert [this] [Long] to [Int] with bounds of [Int.MIN_VALUE] and [Int.MAX_VALUE]
*/
fun Long.toCoercedInt(): Int = coerceIn(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()).toInt()
/**
* Convert [this] [Long] to [Short] with bounds of [Short.MIN_VALUE] and [Short.MAX_VALUE]
*/
fun Long.toCoercedShort(): Short = coerceIn(Short.MIN_VALUE.toLong(), Short.MAX_VALUE.toLong()).toShort()
/**
* Convert [this] [Long] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
*/
fun Long.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toLong(), Byte.MAX_VALUE.toLong()).toByte()
/**
* Convert [this] [Int] to [Short] with bounds of [Short.MIN_VALUE] and [Short.MAX_VALUE]
*/
fun Int.toCoercedShort(): Short = coerceIn(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt()).toShort()
/**
* Convert [this] [Int] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
*/
fun Int.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toInt(), Byte.MAX_VALUE.toInt()).toByte()
/**
* Convert [this] [Short] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
*/
fun Short.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toShort(), Byte.MAX_VALUE.toShort()).toByte()
@@ -0,0 +1,37 @@
package dev.inmo.micro_utils.common
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
@Serializable
@JvmInline
value class Progress private constructor(
val of1: Double
) {
val of1Float
get() = of1.toFloat()
val of100
get() = of1 * 100
val of100Float
get() = of100.toFloat()
val of100Int
get() = of100.toInt()
init {
require(of1 in rangeOfValues) {
"Progress main value should be in $rangeOfValues, but incoming value is $of1"
}
}
companion object {
val rangeOfValues = 0.0 .. 1.0
val START = Progress(rangeOfValues.start)
val COMPLETED = Progress(rangeOfValues.endInclusive)
operator fun invoke(of1: Double) = Progress(of1.coerceIn(rangeOfValues))
operator fun invoke(part: Number, total: Number) = Progress(
part.toDouble() / total.toDouble()
)
}
}
@@ -0,0 +1,80 @@
@file:Suppress(
"RemoveRedundantCallsOfConversionMethods",
"RedundantVisibilityModifier",
)
package dev.inmo.micro_utils.common
import kotlin.Byte
import kotlin.Double
import kotlin.Float
import kotlin.Int
import kotlin.Long
import kotlin.Short
import kotlin.Suppress
public operator fun Progress.plus(other: Progress): Progress = Progress(of1 + other.of1)
public operator fun Progress.minus(other: Progress): Progress = Progress(of1 - other.of1)
public operator fun Progress.plus(i: Byte): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Byte): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Byte): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Byte): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Byte): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Short): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Short): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Short): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Short): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Short): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Int): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Int): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Int): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Int): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Int): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Long): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Long): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Long): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Long): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Long): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Float): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Float): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Float): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Float): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Float): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Double): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Double): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Double): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Double): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Double): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.compareTo(other: Progress): Int = (of1 - other.of1).toInt()
@@ -1,5 +1,27 @@
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(
onFailure: (Throwable) -> Boolean,
action: () -> R
): Result<R> {
do {
runCatching {
action()
}.onFailure {
if (!onFailure(it)) {
return Result.failure(it)
}
}.onSuccess {
return Result.success(it)
}
} while (true)
}
/**
* Executes the given [action] until getting of successful result specified number of [times].
*
@@ -10,12 +32,23 @@ inline fun <R> repeatOnFailure(
onEachFailure: (Throwable) -> Unit = {},
action: (Int) -> R
): Optional<R> {
repeat(times) {
runCatching {
action(it)
}.onFailure(onEachFailure).onSuccess {
return Optional.presented(it)
var i = 0
val result = repeatOnFailure(
{
onEachFailure(it)
if (i < times) {
i++
true
} else {
false
}
}
) {
action(i)
}
return if (result.isSuccess) {
Optional.presented(result.getOrThrow())
} else {
Optional.absent()
}
return Optional.absent()
}
@@ -0,0 +1,6 @@
package dev.inmo.micro_utils.common
val FixedSignsRange = 0 .. 100
expect fun Float.fixed(signs: Int): Float
expect fun Double.fixed(signs: Int): Double
@@ -0,0 +1,4 @@
package dev.inmo.micro_utils.common
actual fun Float.fixed(signs: Int): Float = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toFloat()
actual fun Double.fixed(signs: Int): Double = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toDouble()
@@ -0,0 +1,20 @@
package dev.inmo.micro_utils.common
import java.io.File
import java.io.InputStream
import java.util.UUID
fun InputStream.downloadToTempFile(
fileName: String = UUID.randomUUID().toString(),
fileExtension: String? = ".temp",
folder: File? = null
) = File.createTempFile(
fileName,
fileExtension,
folder
).apply {
outputStream().use {
copyTo(it)
}
deleteOnExit()
}
@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.common
import java.math.BigDecimal
import java.math.RoundingMode
actual fun Float.fixed(signs: Int): Float = BigDecimal.valueOf(this.toDouble())
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
.toFloat();
actual fun Double.fixed(signs: Int): Double = BigDecimal.valueOf(this)
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
.toDouble();
@@ -0,0 +1,36 @@
package dev.inmo.micro_utils.common
import okio.FileSystem
import okio.Path
import okio.use
actual typealias MPPFile = Path
/**
* @suppress
*/
actual val MPPFile.filename: FileName
get() = FileName(toString())
/**
* @suppress
*/
actual val MPPFile.filesize: Long
get() = FileSystem.SYSTEM.openReadOnly(this).use {
it.size()
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
get() = {
FileSystem.SYSTEM.read(this) {
readByteArray()
}
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = {
bytesAllocatorSync()
}
+26
View File
@@ -0,0 +1,26 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import platform.posix.snprintf
import platform.posix.sprintf
actual fun Float.fixed(signs: Int): Float {
return memScoped {
val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toFloat()
}
}
actual fun Double.fixed(signs: Int): Double {
return memScoped {
val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toDouble()
}
}
@@ -0,0 +1,76 @@
package dev.inmo.micro_utils.common
import android.os.Bundle
import android.os.Parcelable
import androidx.fragment.app.Fragment
import java.io.Serializable
import kotlin.reflect.KProperty
object ArgumentPropertyNullableDelegate {
operator fun <T: Any> getValue(thisRef: Fragment, property: KProperty<*>): T? {
val arguments = thisRef.arguments ?: return null
val key = property.name
return when (property.getter.returnType.classifier) {
// Scalars
String::class -> arguments.getString(key)
Boolean::class -> arguments.getBoolean(key)
Byte::class -> arguments.getByte(key)
Char::class -> arguments.getChar(key)
Double::class -> arguments.getDouble(key)
Float::class -> arguments.getFloat(key)
Int::class -> arguments.getInt(key)
Long::class -> arguments.getLong(key)
Short::class -> arguments.getShort(key)
// References
Bundle::class -> arguments.getBundle(key)
CharSequence::class -> arguments.getCharSequence(key)
Parcelable::class -> arguments.getParcelable(key)
// Scalar arrays
BooleanArray::class -> arguments.getBooleanArray(key)
ByteArray::class -> arguments.getByteArray(key)
CharArray::class -> arguments.getCharArray(key)
DoubleArray::class -> arguments.getDoubleArray(key)
FloatArray::class -> arguments.getFloatArray(key)
IntArray::class -> arguments.getIntArray(key)
LongArray::class -> arguments.getLongArray(key)
ShortArray::class -> arguments.getShortArray(key)
Array::class -> {
val componentType = property.returnType.classifier ?.javaClass ?.componentType!!
@Suppress("UNCHECKED_CAST") // Checked by reflection.
when {
Parcelable::class.java.isAssignableFrom(componentType) -> {
arguments.getParcelableArray(key)
}
String::class.java.isAssignableFrom(componentType) -> {
arguments.getStringArray(key)
}
CharSequence::class.java.isAssignableFrom(componentType) -> {
arguments.getCharSequenceArray(key)
}
Serializable::class.java.isAssignableFrom(componentType) -> {
arguments.getSerializable(key)
}
else -> {
val valueType = componentType.canonicalName
throw IllegalArgumentException(
"Illegal value array type $valueType for key \"$key\""
)
}
}
}
Serializable::class -> arguments.getSerializable(key)
else -> null
} as? T
}
}
object ArgumentPropertyNonNullableDelegate {
operator fun <T: Any> getValue(thisRef: Fragment, property: KProperty<*>): T {
return ArgumentPropertyNullableDelegate.getValue<T>(thisRef, property)!!
}
}
fun argumentOrNull() = ArgumentPropertyNullableDelegate
fun argumentOrThrow() = ArgumentPropertyNonNullableDelegate
@@ -0,0 +1,61 @@
package dev.inmo.micro_utils.common
import android.app.Activity
import android.view.View
import android.view.ViewGroup
import androidx.core.view.children
import androidx.fragment.app.Fragment
fun findViewsByTag(viewGroup: ViewGroup, tag: Any?): List<View> {
return viewGroup.children.flatMap {
findViewsByTag(it, tag)
}.toList()
}
fun findViewsByTag(viewGroup: ViewGroup, key: Int, tag: Any?): List<View> {
return viewGroup.children.flatMap {
findViewsByTag(it, key, tag)
}.toList()
}
fun findViewsByTag(view: View, tag: Any?): List<View> {
val result = mutableListOf<View>()
if (view.tag == tag) {
result.add(view)
}
if (view is ViewGroup) {
result.addAll(findViewsByTag(view, tag))
}
return result.toList()
}
fun findViewsByTag(view: View, key: Int, tag: Any?): List<View> {
val result = mutableListOf<View>()
if (view.getTag(key) == tag) {
result.add(view)
}
if (view is ViewGroup) {
result.addAll(findViewsByTag(view, key, tag))
}
return result.toList()
}
fun Activity.findViewsByTag(tag: Any?) = rootView ?.let {
findViewsByTag(it, tag)
}
fun Activity.findViewsByTag(key: Int, tag: Any?) = rootView ?.let {
findViewsByTag(it, key, tag)
}
fun Fragment.findViewsByTag(tag: Any?) = view ?.let {
findViewsByTag(it, tag)
}
fun Fragment.findViewsByTag(key: Int, tag: Any?) = view ?.let {
findViewsByTag(it, key, tag)
}
fun Fragment.findViewsByTagInActivity(tag: Any?) = activity ?.findViewsByTag(tag)
fun Fragment.findViewsByTagInActivity(key: Int, tag: Any?) = activity ?.findViewsByTag(key, tag)
@@ -0,0 +1,7 @@
package dev.inmo.micro_utils.common
import android.app.Activity
import android.view.View
val Activity.rootView: View?
get() = findViewById<View?>(android.R.id.content) ?.rootView ?: window.decorView.findViewById<View?>(android.R.id.content) ?.rootView
@@ -0,0 +1,36 @@
package dev.inmo.micro_utils.common
import okio.FileSystem
import okio.Path
import okio.use
actual typealias MPPFile = Path
/**
* @suppress
*/
actual val MPPFile.filename: FileName
get() = FileName(toString())
/**
* @suppress
*/
actual val MPPFile.filesize: Long
get() = FileSystem.SYSTEM.openReadOnly(this).use {
it.size()
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
get() = {
FileSystem.SYSTEM.read(this) {
readByteArray()
}
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = {
bytesAllocatorSync()
}
+26
View File
@@ -0,0 +1,26 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import platform.posix.snprintf
import platform.posix.sprintf
actual fun Float.fixed(signs: Int): Float {
return memScoped {
val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toFloat()
}
}
actual fun Double.fixed(signs: Int): Double {
return memScoped {
val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toDouble()
}
}
+1
View File
@@ -22,6 +22,7 @@ kotlin {
dependencies {
api libs.kt.coroutines.android
}
dependsOn(jvmMain)
}
}
}
@@ -3,24 +3,58 @@ package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateList
import dev.inmo.micro_utils.common.applyDiff
import dev.inmo.micro_utils.coroutines.ExceptionHandler
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandlerWithNull
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext
/**
* Each value of [this] [Flow] will trigger [applyDiff] to the result [SnapshotStateList]
*
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [SnapshotStateList]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <reified T> Flow<List<T>>.asMutableComposeListState(
scope: CoroutineScope
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
noinline onException: ExceptionHandler<List<T>?> = defaultSafelyWithoutExceptionHandlerWithNull,
): SnapshotStateList<T> {
val state = mutableStateListOf<T>()
subscribeSafelyWithoutExceptions(scope) {
val changeBlock: suspend (List<T>) -> Unit = useContextOnChange ?.let {
{
withContext(useContextOnChange) {
state.applyDiff(it)
}
}
} ?: {
state.applyDiff(it)
}
subscribeSafelyWithoutExceptions(scope, onException, changeBlock)
return state
}
/**
* In fact, it is just classcast of [asMutableComposeListState] to [List]
*
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [List]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*
* @return Changing in time [List] which follow [Flow] values
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <reified T> Flow<List<T>>.asComposeList(
scope: CoroutineScope
): List<T> = asMutableComposeListState(scope)
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
noinline onException: ExceptionHandler<List<T>?> = defaultSafelyWithoutExceptionHandlerWithNull,
): List<T> = asMutableComposeListState(scope, useContextOnChange, onException)
@@ -1,35 +1,94 @@
package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.*
import dev.inmo.micro_utils.common.compose.asState
import dev.inmo.micro_utils.coroutines.ExceptionHandler
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandlerWithNull
import dev.inmo.micro_utils.coroutines.doInUI
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext
/**
* Will map [this] [Flow] as [MutableState]. Returned [MutableState] WILL NOT change source [Flow]
*
* @param initial First value which will be passed to the result [MutableState]
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [MutableState]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
fun <T> Flow<T>.asMutableComposeState(
initial: T,
scope: CoroutineScope
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
): MutableState<T> {
val state = mutableStateOf(initial)
subscribeSafelyWithoutExceptions(scope) { state.value = it }
val changeBlock: suspend (T) -> Unit = useContextOnChange ?.let {
{
withContext(useContextOnChange) {
state.value = it
}
}
} ?: {
state.value = it
}
subscribeSafelyWithoutExceptions(scope, onException, block = changeBlock)
return state
}
/**
* Will map [this] [StateFlow] as [MutableState]. Returned [MutableState] WILL NOT change source [StateFlow].
* This conversation will pass its [StateFlow.value] as the first value
*
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [MutableState]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <T> StateFlow<T>.asMutableComposeState(
scope: CoroutineScope
): MutableState<T> = asMutableComposeState(value, scope)
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
): MutableState<T> = asMutableComposeState(value, scope, useContextOnChange, onException)
/**
* Will create [MutableState] using [asMutableComposeState] and use [asState] to convert it as immutable state
*
* @param initial First value which will be passed to the result [State]
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [State]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
fun <T> Flow<T>.asComposeState(
initial: T,
scope: CoroutineScope
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
): State<T> {
val state = asMutableComposeState(initial, scope)
return derivedStateOf { state.value }
val state = asMutableComposeState(initial, scope, useContextOnChange, onException)
return state.asState()
}
/**
* Will map [this] [StateFlow] as [State]. This conversation will pass its [StateFlow.value] as the first value
*
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [State]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <T> StateFlow<T>.asComposeState(
scope: CoroutineScope
): State<T> = asComposeState(value, scope)
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
): State<T> = asComposeState(value, scope, useContextOnChange, onException)
@@ -1,23 +0,0 @@
package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
fun <T> Flow<T>.toMutableState(
initial: T,
scope: CoroutineScope
): MutableState<T> {
val state = mutableStateOf(initial)
subscribeSafelyWithoutExceptions(scope) { state.value = it }
return state
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T> StateFlow<T>.toMutableState(
scope: CoroutineScope
): MutableState<T> = toMutableState(value, scope)
@@ -1,19 +1,15 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.consumeAsFlow
fun <T> CoroutineScope.actor(
channelCapacity: Int = Channel.UNLIMITED,
block: suspend (T) -> Unit
): Channel<T> {
val channel = Channel<T>(channelCapacity)
launch {
for (data in channel) {
block(data)
}
}
channel.consumeAsFlow().subscribe(this, block)
return channel
}
@@ -0,0 +1,30 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.consumeAsFlow
fun <T> CoroutineScope.actorAsync(
channelCapacity: Int = Channel.UNLIMITED,
markerFactory: suspend (T) -> Any? = { null },
block: suspend (T) -> Unit
): Channel<T> {
val channel = Channel<T>(channelCapacity)
channel.consumeAsFlow().subscribeAsync(this, markerFactory, block)
return channel
}
inline fun <T> CoroutineScope.safeActorAsync(
channelCapacity: Int = Channel.UNLIMITED,
noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
noinline markerFactory: suspend (T) -> Any? = { null },
crossinline block: suspend (T) -> Unit
): Channel<T> = actorAsync(
channelCapacity,
markerFactory
) {
safely(onException) {
block(it)
}
}
@@ -6,23 +6,19 @@ import kotlin.coroutines.*
suspend fun <T> Iterable<Deferred<T>>.awaitFirstWithDeferred(
scope: CoroutineScope,
cancelOnResult: Boolean = true
): Pair<Deferred<T>, T> = suspendCoroutine<Pair<Deferred<T>, T>> { continuation ->
scope.launch(SupervisorJob()) {
val scope = this
forEach {
scope.launch {
continuation.resume(it to it.await())
scope.cancel()
}
): Pair<Deferred<T>, T> {
val resultDeferred = CompletableDeferred<Pair<Deferred<T>, T>>()
val scope = scope.LinkedSupervisorScope()
forEach {
scope.launch {
resultDeferred.complete(it to it.await())
scope.cancel()
}
}
}.also {
if (cancelOnResult) {
forEach {
try {
it.cancel()
} catch (e: IllegalStateException) {
e.printStackTrace()
return resultDeferred.await().also {
if (cancelOnResult) {
forEach {
runCatchingSafely { it.cancel() }
}
}
}
@@ -0,0 +1,39 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.flow.*
import kotlin.js.JsName
import kotlin.jvm.JvmName
inline fun <T, R> Flow<Flow<T>>.flatMap(
crossinline mapper: suspend (T) -> R
) = flow {
collect {
it.collect {
emit(mapper(it))
}
}
}
@JsName("flatMapIterable")
@JvmName("flatMapIterable")
inline fun <T, R> Flow<Iterable<T>>.flatMap(
crossinline mapper: suspend (T) -> R
) = map {
it.asFlow()
}.flatMap(mapper)
inline fun <T, R> Flow<Flow<T>>.flatMapNotNull(
crossinline mapper: suspend (T) -> R
) = flatMap(mapper).takeNotNull()
@JsName("flatMapNotNullIterable")
@JvmName("flatMapNotNullIterable")
inline fun <T, R> Flow<Iterable<T>>.flatMapNotNull(
crossinline mapper: suspend (T) -> R
) = flatMap(mapper).takeNotNull()
fun <T> Flow<Flow<T>>.flatten() = flatMap { it }
@JsName("flattenIterable")
@JvmName("flattenIterable")
fun <T> Flow<Iterable<T>>.flatten() = flatMap { it }
@@ -0,0 +1,6 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.flow.*
fun <T> Flow<T>.takeNotNull() = mapNotNull { it }
fun <T> Flow<T>.filterNotNull() = takeNotNull()
@@ -115,10 +115,21 @@ suspend inline fun <T> runCatchingSafely(
safely(onException, block)
}
suspend inline fun <T, R> T.runCatchingSafely(
noinline onException: ExceptionHandler<R> = defaultSafelyExceptionHandler,
noinline block: suspend T.() -> R
): Result<R> = runCatching {
safely(onException) { block() }
}
suspend inline fun <T> safelyWithResult(
noinline block: suspend CoroutineScope.() -> T
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
suspend inline fun <T, R> T.safelyWithResult(
noinline block: suspend T.() -> R
): Result<R> = runCatchingSafely(defaultSafelyExceptionHandler, block)
/**
* Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and
* returning null at one time
@@ -0,0 +1,26 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import org.w3c.dom.Image
suspend fun preloadImage(src: String): Image {
val image = Image()
image.src = src
val job = Job()
image.addEventListener("load", {
runCatching { job.complete() }
})
runCatchingSafely {
job.join()
}.onFailure {
if (it is CancellationException) {
image.src = ""
}
}.getOrThrow()
return image
}
@@ -0,0 +1,9 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
fun <T> launchInCurrentThread(block: suspend CoroutineScope.() -> T): T {
val scope = CoroutineScope(Dispatchers.Unconfined)
return scope.launchSynchronously(block)
}
@@ -6,7 +6,7 @@ fun <T> CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T
var result: Result<T>? = null
val objectToSynchronize = Object()
synchronized(objectToSynchronize) {
launch {
launch(start = CoroutineStart.UNDISPATCHED) {
result = safelyWithResult(block)
}.invokeOnCompletion {
synchronized(objectToSynchronize) {
@@ -0,0 +1,47 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlin.test.Test
import kotlin.test.assertEquals
class LaunchInCurrentThreadTests {
@Test
fun simpleTestThatLaunchInCurrentThreadWorks() {
val expectedResult = 10
val result = launchInCurrentThread {
expectedResult
}
assertEquals(expectedResult, result)
}
@Test
fun simpleTestThatSeveralLaunchInCurrentThreadWorks() {
val testData = 0 until 100
testData.forEach {
val result = launchInCurrentThread {
it
}
assertEquals(it, result)
}
}
@Test
fun simpleTestThatLaunchInCurrentThreadWillCorrectlyHandleSuspensionsWorks() {
val testData = 0 until 100
suspend fun test(data: Any): Any {
return withContext(Dispatchers.Default) {
delay(1)
data
}
}
testData.forEach {
val result = launchInCurrentThread {
test(it)
}
assertEquals(it, result)
}
}
}
@@ -0,0 +1,50 @@
package dev.inmo.micro_utils.coroutines
import android.view.View
import android.view.ViewGroup
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
/**
* [kotlinx.coroutines.flow.Flow]-based [android.view.ViewGroup.OnHierarchyChangeListener]
*
* @param recursive If set, any call of [onChildViewAdded] will check if child [View] is [ViewGroup] and subscribe to this
* [ViewGroup] too
* @param [_onChildViewAdded] Internal [MutableSharedFlow] which will be used to pass data to [onChildViewAdded] flow
* @param [_onChildViewRemoved] Internal [MutableSharedFlow] which will be used to pass data to [onChildViewRemoved] flow
*/
class FlowOnHierarchyChangeListener(
private val recursive: Boolean = false,
private val _onChildViewAdded: MutableSharedFlow<Pair<View, View>> = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE),
private val _onChildViewRemoved: MutableSharedFlow<Pair<View, View>> = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE)
) : ViewGroup.OnHierarchyChangeListener {
val onChildViewAdded = _onChildViewAdded.asSharedFlow()
val onChildViewRemoved = _onChildViewRemoved.asSharedFlow()
/**
* Will emit data into [onChildViewAdded] flow. If [recursive] is true and [child] is [ViewGroup] will also
* subscribe to [child] hierarchy changes.
*
* Due to the fact that this method is not suspendable, [FlowOnHierarchyChangeListener] will use
* [MutableSharedFlow.tryEmit] to send data into [_onChildViewAdded]. That is why its default extraBufferCapacity is
* [Int.MAX_VALUE]
*/
override fun onChildViewAdded(parent: View, child: View) {
_onChildViewAdded.tryEmit(parent to child)
if (recursive && child is ViewGroup) {
child.setOnHierarchyChangeListener(this)
}
}
/**
* Just emit data into [onChildViewRemoved]
*
* Due to the fact that this method is not suspendable, [FlowOnHierarchyChangeListener] will use
* [MutableSharedFlow.tryEmit] to send data into [_onChildViewRemoved]. That is why its default extraBufferCapacity is
* [Int.MAX_VALUE]
*/
override fun onChildViewRemoved(parent: View, child: View) {
_onChildViewRemoved.tryEmit(parent to child)
}
}
@@ -0,0 +1,17 @@
package dev.inmo.micro_utils.coroutines
import android.view.ViewGroup
import android.view.ViewGroup.OnHierarchyChangeListener
/**
* Use [ViewGroup.setOnHierarchyChangeListener] recursively for all available [ViewGroup]s starting with [this].
* This extension DO NOT guarantee that recursive subscription will happen after this method call
*/
fun ViewGroup.setOnHierarchyChangeListenerRecursively(
listener: OnHierarchyChangeListener
) {
setOnHierarchyChangeListener(listener)
(0 until childCount).forEach {
(getChildAt(it) as? ViewGroup) ?.setOnHierarchyChangeListenerRecursively(listener)
}
}
+1
View File
@@ -11,6 +11,7 @@ kotlin {
commonMain {
dependencies {
api project(":micro_utils.common")
api libs.krypto
}
}
jsMain {
@@ -1,6 +1,8 @@
package dev.inmo.micro_utils.crypto
import com.soywiz.krypto.md5
typealias MD5 = String
expect fun SourceBytes.md5(): MD5
fun SourceString.md5(): MD5 = encodeToByteArray().md5()
fun SourceBytes.md5(): MD5 = md5().hexLower
fun SourceString.md5(): MD5 = encodeToByteArray().md5().hexLower
@@ -1,6 +0,0 @@
package dev.inmo.micro_utils.crypto
/**
* @suppress
*/
actual fun SourceBytes.md5(): MD5 = CryptoJS.MD5(decodeToString())
@@ -1,12 +0,0 @@
package dev.inmo.micro_utils.crypto
import java.math.BigInteger
import java.security.MessageDigest
/**
* @suppress
*/
actual fun SourceBytes.md5(): MD5 = BigInteger(
1,
MessageDigest.getInstance("MD5").digest(this)
).toString(16)
-31
View File
@@ -1,30 +1,5 @@
apply plugin: 'com.getkeepsafe.dexcount'
ext {
jvmKotlinFolderFile = {
String sep = File.separator
return new File("${project.projectDir}${sep}src${sep}jvmMain${sep}kotlin")
}
enableIncludingJvmCodeInAndroidPart = {
File jvmKotlinFolder = jvmKotlinFolderFile()
if (jvmKotlinFolder.exists()) {
android.sourceSets.main.java.srcDirs += jvmKotlinFolder.path
}
}
disableIncludingJvmCodeInAndroidPart = {
File jvmKotlinFolder = jvmKotlinFolderFile()
String[] oldDirs = android.sourceSets.main.java.srcDirs
android.sourceSets.main.java.srcDirs = []
for (oldDir in oldDirs) {
if (oldDir != jvmKotlinFolder.path) {
android.sourceSets.main.java.srcDirs += oldDir
}
}
}
}
android {
compileSdkVersion libs.versions.android.props.compileSdk.get().toInteger()
buildToolsVersion libs.versions.android.props.buildTools.get()
@@ -58,10 +33,4 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
sourceSets {
String sep = File.separator
main.java.srcDirs += "src${sep}main${sep}kotlin"
enableIncludingJvmCodeInAndroidPart()
}
}
+2 -2
View File
@@ -121,11 +121,11 @@ tasks.dokkaHtml {
// }
named("jvmMain") {
sourceRoots.setFrom(findSourcesWithName("jvmMain", "commonMain"))
sourceRoots.setFrom(findSourcesWithName("jvmMain"))
}
named("androidMain") {
sourceRoots.setFrom(findSourcesWithName("androidMain", "commonMain"))
sourceRoots.setFrom(findSourcesWithName("androidMain"))
}
}
}
+2 -1
View File
@@ -23,11 +23,12 @@ allprojects {
mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization.gradle"
mppProjectWithSerializationAndComposePresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerializationAndCompose.gradle"
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle"
mppJvmJsLinuxMingwProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsLinuxMingwProject.gradle"
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle"
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle"
publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle"
publishMavenPath = "${rootProject.projectDir.absolutePath}/maven.publish.gradle"
publishJvmOnlyPath = "${rootProject.projectDir.absolutePath}/jvm.publish.gradle"
}
}
@@ -48,8 +48,8 @@ interface DefaultStatesManagerRepo<T : State> {
*/
open class DefaultStatesManager<T : State>(
protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(),
protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> true },
protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> false },
protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> false }
) : StatesManager<T> {
protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0)
override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow()
+2 -2
View File
@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
# Project data
group=dev.inmo
version=0.12.0
android_code_version=139
version=0.18.0
android_code_version=191
+42 -18
View File
@@ -1,36 +1,47 @@
[versions]
kt = "1.7.0"
kt-serialization = "1.4.0-RC"
kt = "1.8.20"
kt-serialization = "1.5.0"
kt-coroutines = "1.6.4"
jb-compose = "1.2.0-alpha01-dev753"
jb-exposed = "0.39.2"
jb-dokka = "1.7.0"
kslog = "1.1.1"
klock = "3.0.0"
uuid = "0.5.0"
jb-compose = "1.4.0"
jb-exposed = "0.41.1"
jb-dokka = "1.8.10"
ktor = "2.0.3"
korlibs = "3.4.0"
uuid = "0.7.0"
ktor = "2.3.0"
gh-release = "2.4.1"
android-gradle = "7.2.2"
dexcount = "3.1.0"
koin = "3.4.0"
android-coreKtx = "1.8.0"
android-recyclerView = "1.2.1"
android-appCompat = "1.4.2"
android-espresso = "3.4.0"
android-test = "1.1.3"
okio = "3.3.0"
ksp = "1.8.20-1.0.11"
kotlin-poet = "1.13.0"
android-gradle = "7.4.2"
dexcount = "4.0.0"
android-coreKtx = "1.10.0"
android-recyclerView = "1.3.0"
android-appCompat = "1.6.1"
android-fragment = "1.5.7"
android-espresso = "3.5.1"
android-test = "1.1.5"
android-props-minSdk = "21"
android-props-compileSdk = "32"
android-props-buildTools = "32.0.0"
android-props-compileSdk = "33"
android-props-buildTools = "33.0.2"
[libraries]
kt-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kt" }
kt-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kt" }
kt-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kt-serialization" }
kt-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-cbor", version.ref = "kt-serialization" }
@@ -56,9 +67,12 @@ ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.re
ktor-server-statusPages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" }
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "korlibs" }
krypto = { module = "com.soywiz.korlibs.krypto:krypto", version.ref = "korlibs" }
uuid = { module = "com.benasher44:uuid", version.ref = "uuid" }
koin = { module = "io.insert-koin:koin-core", version.ref = "koin" }
jb-exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "jb-exposed" }
@@ -67,6 +81,7 @@ jb-exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "jb-
android-coreKtx = { module = "androidx.core:core-ktx", version.ref = "android-coreKtx" }
android-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "android-recyclerView" }
android-appCompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "android-appCompat" }
android-fragment = { module = "androidx.fragment:fragment", version.ref = "android-fragment" }
android-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "android-espresso" }
android-test-junit = { module = "androidx.test.ext:junit", version.ref = "android-test" }
@@ -74,9 +89,18 @@ android-test-junit = { module = "androidx.test.ext:junit", version.ref = "androi
kt-test-js = { module = "org.jetbrains.kotlin:kotlin-test-js", version.ref = "kt" }
kt-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kt" }
# ksp dependencies
kotlin-poet = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin-poet" }
ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
# Buildscript
buildscript-kt-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kt" }
buildscript-kt-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kt" }
buildscript-kt-ksp = { module = "com.google.devtools.ksp:symbol-processing-gradle-plugin", version.ref = "ksp" }
buildscript-jb-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "jb-dokka" }
buildscript-gh-release = { module = "com.github.breadmoirai:github-release", version.ref = "gh-release" }
buildscript-android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle" }
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+118
View File
@@ -0,0 +1,118 @@
apply plugin: 'maven-publish'
task javadocJar(type: Jar) {
from javadoc
classifier = 'javadoc'
}
task sourcesJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'sources'
}
publishing {
publications {
maven(MavenPublication) {
from components.java
artifact javadocJar
artifact sourcesJar
pom {
resolveStrategy = Closure.DELEGATE_FIRST
description = "It is set of projects with micro tools for avoiding of routines coding"
name = "${project.name}"
url = "https://github.com/InsanusMokrassar/MicroUtils/"
scm {
developerConnection = "scm:git:[fetch=]https://github.com/InsanusMokrassar/MicroUtils.git[push=]https://github.com/InsanusMokrassar/MicroUtils.git"
url = "https://github.com/InsanusMokrassar/MicroUtils.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Aleksei Ovsiannikov"
email = "ovsyannikov.alexey95@gmail.com"
}
developer {
id = "000Sanya"
name = "Syrov Aleksandr"
email = "000sanya.000sanya@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"
}
}
}
repositories {
if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) {
maven {
name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/MicroUtils")
credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
}
}
}
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) {
maven {
name = "Gitea"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven")
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN')
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
}
}
}
}
}
}
}
if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing'
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)
}
}
}
+1
View File
@@ -0,0 +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","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":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}},"type":"JVM"}
+28
View File
@@ -0,0 +1,28 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api libs.koin
api libs.uuid
}
}
jvmMain {
dependencies {
api libs.kt.reflect
}
}
androidMain {
dependencies {
api libs.kt.reflect
}
}
}
}
+100
View File
@@ -0,0 +1,100 @@
# Koin generator
It is Kotlin Symbol Processing generator for `Koin` module in `MicroUtils`.
1. [What may do this generator](#what-may-do-this-generator)
2. [How to add generator](#how-to-add-generator)
## What may do this generator
Let's imagine you want to have shortcuts in koin, to get something easily:
```kotlin
val koin: Koin// some initialization
val someUrl = koin.serverUrl
```
So, in that case you may mark containing file with next annotation (in the beginning of file):
```kotlin
@file:GenerateKoinDefinition("serverUrl", String::class, nullable = false)
```
If file is called like `Sample.kt`, will be generated file `GeneratedDefinitionsSample.kt` with next content:
```kotlin
public val Scope.serverUrl: String
get() = get(named("serverUrl"))
public val Koin.serverUrl: String
get() = get(named("serverUrl"))
public fun Module.serverUrlSingle(createdAtStart: Boolean = false,
definition: Definition<String>): KoinDefinition<String> =
single(named("serverUrl"), createdAtStart = createdAtStart, definition = definition)
public fun Module.serverUrlFactory(definition: Definition<String>):
KoinDefinition<String> = factory(named("serverUrl"), definition = definition)
```
Besides, you may use the generics:
```kotlin
@file:GenerateKoinDefinition("sampleInfo", Sample::class, G1::class, G2::class, nullable = false)
```
Will generate:
```kotlin
public val Scope.sampleInfo: Sample<G1, G2>
get() = get(named("sampleInfo"))
public val Koin.sampleInfo: Sample<G1, G2>
get() = get(named("sampleInfo"))
public fun Module.sampleInfoSingle(createdAtStart: Boolean = false,
definition: Definition<Sample<G1, G2>>): KoinDefinition<Sample<G1, G2>> =
single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition)
public fun Module.sampleInfoFactory(definition: Definition<Sample<G1, G2>>):
KoinDefinition<Sample<G1, G2>> = factory(named("sampleInfo"), definition = definition)
```
In case you wish not to generate single:
```kotlin
@file:GenerateKoinDefinition("sampleInfo", Sample::class, G1::class, G2::class, nullable = false, generateSingle = false)
```
And you will take next code:
```kotlin
public val Scope.sampleInfo: Sample<G1, G2>
get() = get(named("sampleInfo"))
public val Koin.sampleInfo: Sample<G1, G2>
get() = get(named("sampleInfo"))
public fun Module.sampleInfoFactory(definition: Definition<Sample<G1, G2>>):
KoinDefinition<Sample<G1, G2>> = factory(named("sampleInfo"), definition = definition)
```
## How to add generator
**Note: $ksp_version in the samples above is equal to supported `ksp` version presented in `/gradle/libs.versions.toml` of project**
**Note: $microutils_version in the version of MicroUtils library in your project**
1. Add `classpath` in `build.gradle` (`classpath "com.google.devtools.ksp:symbol-processing-gradle-plugin:$ksp_version"`)
2. Add plugin to the plugins list of your module: `id "com.google.devtools.ksp"`
3. In `dependencies` block add to the required target/compile the dependency `dev.inmo:micro_utils.koin.generator:$microutils_version`:
```groovy
dependencies {
add("kspCommonMainMetadata", "dev.inmo:micro_utils.koin.generator:$microutils_version") // will work in commonMain of your multiplatform module
add("kspJvm", "dev.inmo:micro_utils.koin.generator:$microutils_version") // will work in main of your JVM module
}
ksp { // this generator do not require any arguments and we should left `ksp` empty
}
```
+15
View File
@@ -0,0 +1,15 @@
plugins {
id "org.jetbrains.kotlin.jvm"
}
apply from: "$publishJvmOnlyPath"
repositories {
mavenCentral()
}
dependencies {
api project(":micro_utils.koin")
api libs.kotlin.poet
api libs.ksp
}
+178
View File
@@ -0,0 +1,178 @@
package dev.inmo.micro_utils.koin.generator
import com.google.devtools.ksp.KSTypeNotPresentException
import com.google.devtools.ksp.KSTypesNotPresentException
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSFile
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.toTypeName
import com.squareup.kotlinpoet.ksp.writeTo
import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition
import org.koin.core.Koin
import org.koin.core.module.Module
import org.koin.core.scope.Scope
import java.io.File
import kotlin.reflect.KClass
class Processor(
private val codeGenerator: CodeGenerator
) : SymbolProcessor {
private val definitionClassName = ClassName("org.koin.core.definition", "Definition")
private val koinDefinitionClassName = ClassName("org.koin.core.definition", "KoinDefinition")
@OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
resolver.getSymbolsWithAnnotation(
GenerateKoinDefinition::class.qualifiedName!!
).filterIsInstance<KSFile>().forEach { ksFile ->
FileSpec.builder(
ksFile.packageName.asString(),
"GeneratedDefinitions${ksFile.fileName.removeSuffix(".kt")}"
).apply {
addFileComment(
"""
THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
TO REGENERATE IT JUST DELETE FILE
ORIGINAL FILE: ${ksFile.fileName}
""".trimIndent()
)
ksFile.getAnnotationsByType(GenerateKoinDefinition::class).forEach {
val type = runCatching {
it.type.asTypeName()
}.getOrElse { e ->
if (e is KSTypeNotPresentException) {
e.ksType.toClassName()
} else {
throw e
}
}
val targetType = runCatching {
type.parameterizedBy(*(it.typeArgs.takeIf { it.isNotEmpty() } ?.map { it.asTypeName() } ?.toTypedArray() ?: return@runCatching type))
}.getOrElse { e ->
when (e) {
is KSTypeNotPresentException -> e.ksType.toClassName()
}
if (e is KSTypesNotPresentException) {
type.parameterizedBy(*e.ksTypes.map { it.toTypeName() }.toTypedArray())
} else {
throw e
}
}.copy(
nullable = it.nullable
)
fun addGetterProperty(
receiver: KClass<*>
) {
addProperty(
PropertySpec.builder(
it.name,
targetType,
).apply {
addKdoc(
"""
@return Definition by key "${it.name}"
""".trimIndent()
)
getter(
FunSpec.getterBuilder().apply {
addCode(
"return " + (if (it.nullable) {
"getOrNull"
} else {
"get"
}) + "(named(\"${it.name}\"))"
)
}.build()
)
receiver(receiver)
}.build()
)
}
addGetterProperty(Scope::class)
addGetterProperty(Koin::class)
if (it.generateSingle) {
addFunction(
FunSpec.builder("${it.name}Single").apply {
addKdoc(
"""
Will register [definition] with [org.koin.core.module.Module.single] and key "${it.name}"
""".trimIndent()
)
receiver(Module::class)
addParameter(
ParameterSpec.builder(
"createdAtStart",
Boolean::class
).apply {
defaultValue("false")
}.build()
)
addParameter(
ParameterSpec.builder(
"definition",
definitionClassName.parameterizedBy(targetType.copy(nullable = false))
).build()
)
returns(koinDefinitionClassName.parameterizedBy(targetType.copy(nullable = false)))
addCode(
"return single(named(\"${it.name}\"), createdAtStart = createdAtStart, definition = definition)"
)
}.build()
)
}
if (it.generateFactory) {
addFunction(
FunSpec.builder("${it.name}Factory").apply {
addKdoc(
"""
Will register [definition] with [org.koin.core.module.Module.factory] and key "${it.name}"
""".trimIndent()
)
receiver(Module::class)
addParameter(
ParameterSpec.builder(
"definition",
definitionClassName.parameterizedBy(targetType.copy(nullable = false))
).build()
)
returns(koinDefinitionClassName.parameterizedBy(targetType.copy(nullable = false)))
addCode(
"return factory(named(\"${it.name}\"), definition = definition)"
)
}.build()
)
}
addImport("org.koin.core.qualifier", "named")
}
}.build().let {
File(
File(ksFile.filePath).parent,
"GeneratedDefinitions${ksFile.fileName}"
).takeIf { !it.exists() } ?.apply {
parentFile.mkdirs()
writer().use { writer ->
it.writeTo(writer)
}
}
}
}
return emptyList()
}
}
@@ -0,0 +1,11 @@
package dev.inmo.micro_utils.koin.generator
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
class Provider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = Processor(
environment.codeGenerator
)
}
@@ -0,0 +1 @@
dev.inmo.micro_utils.koin.generator.Provider
+27
View File
@@ -0,0 +1,27 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
id "com.google.devtools.ksp"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":micro_utils.koin")
}
}
}
}
dependencies {
add("kspCommonMainMetadata", project(":micro_utils.koin.generator"))
}
ksp {
}
@@ -0,0 +1,38 @@
// THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
// TO REGENERATE IT JUST DELETE FILE
// ORIGINAL FILE: Test.kt
package dev.inmo.micro_utils.koin.generator.test
import kotlin.Boolean
import kotlin.String
import org.koin.core.Koin
import org.koin.core.definition.Definition
import org.koin.core.definition.KoinDefinition
import org.koin.core.module.Module
import org.koin.core.qualifier.named
import org.koin.core.scope.Scope
/**
* @return Definition by key "sampleInfo"
*/
public val Scope.sampleInfo: Test<String>
get() = get(named("sampleInfo"))
/**
* @return Definition by key "sampleInfo"
*/
public val Koin.sampleInfo: Test<String>
get() = get(named("sampleInfo"))
/**
* Will register [definition] with [org.koin.core.module.Module.single] and key "sampleInfo"
*/
public fun Module.sampleInfoSingle(createdAtStart: Boolean = false,
definition: Definition<Test<String>>): KoinDefinition<Test<String>> =
single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition)
/**
* Will register [definition] with [org.koin.core.module.Module.factory] and key "sampleInfo"
*/
public fun Module.sampleInfoFactory(definition: Definition<Test<String>>):
KoinDefinition<Test<String>> = factory(named("sampleInfo"), definition = definition)
@@ -0,0 +1,13 @@
@file:GenerateKoinDefinition("sampleInfo", Test::class, String::class, nullable = false)
package dev.inmo.micro_utils.koin.generator.test
import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition
import org.koin.core.Koin
class Test<T>(
koin: Koin
) {
init {
koin.sampleInfo
}
}
@@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.koin.generator.test"/>
@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.koin
import org.koin.core.definition.Definition
import org.koin.core.module.Module
/**
* Will be useful in case you need to declare some singles with one type several types, but need to separate them and do
* not care about how :)
*/
inline fun <reified T : Any> Module.factoryWithRandomQualifier(
noinline definition: Definition<T>
) = factory(RandomQualifier(), definition)
@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.koin
import org.koin.core.definition.Definition
import org.koin.core.module.Module
import org.koin.core.qualifier.StringQualifier
inline fun <reified T : Any> Module.factory(
qualifier: String,
noinline definition: Definition<T>
) = factory(StringQualifier(qualifier), definition)
@@ -0,0 +1,8 @@
package dev.inmo.micro_utils.koin
import org.koin.core.Koin
import org.koin.core.scope.Scope
inline fun <reified T : Any> Scope.getAllDistinct() = getAll<T>().distinct()
inline fun <reified T : Any> Koin.getAllDistinct() = getAll<T>().distinct()
+8
View File
@@ -0,0 +1,8 @@
package dev.inmo.micro_utils.koin
import org.koin.core.Koin
import org.koin.core.scope.Scope
inline fun <reified T : Any> Scope.getAny() = getAll<T>().first()
inline fun <reified T : Any> Koin.getAny() = getAll<T>().first()
@@ -0,0 +1,6 @@
package dev.inmo.micro_utils.koin
import com.benasher44.uuid.uuid4
import org.koin.core.qualifier.StringQualifier
fun RandomQualifier(randomFun: () -> String = { uuid4().toString() }) = StringQualifier(randomFun())
@@ -0,0 +1,13 @@
package dev.inmo.micro_utils.koin
import org.koin.core.definition.Definition
import org.koin.core.module.Module
/**
* Will be useful in case you need to declare some singles with one type several types, but need to separate them and do
* not care about how :)
*/
inline fun <reified T : Any> Module.singleWithRandomQualifier(
createdAtStart: Boolean = false,
noinline definition: Definition<T>
) = single(RandomQualifier(), createdAtStart, definition)
@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.koin
import org.koin.core.definition.Definition
import org.koin.core.module.Module
import org.koin.core.qualifier.StringQualifier
inline fun <reified T : Any> Module.single(
qualifier: String,
createdAtStart: Boolean = false,
noinline definition: Definition<T>
) = single(StringQualifier(qualifier), createdAtStart, definition)
@@ -0,0 +1,28 @@
package dev.inmo.micro_utils.koin.annotations
import kotlin.reflect.KClass
/**
* Use this annotation to mark files near to which generator should place generated extensions for koin [org.koin.core.scope.Scope]
* and [org.koin.core.Koin]
*
* @param name Name for definitions. This name will be available as extension for [org.koin.core.scope.Scope] and [org.koin.core.Koin]
* @param type Type of extensions. It is base star-typed class
* @param typeArgs Generic types for [type]. For example, if [type] == `Something::class` and [typeArgs] == `G1::class,
* G2::class`, the result type will be `Something<G1, G2>`
* @param nullable In case when true, extension will not throw error when definition has not been registered in koin
* @param generateSingle Generate definition factory with [org.koin.core.module.Module.single]. You will be able to use
* the extension [org.koin.core.module.Module].[name]Single(createdAtStart/* default false */) { /* your definition */ }
* @param generateFactory Generate definition factory with [org.koin.core.module.Module.factory]. You will be able to use
* the extension [org.koin.core.module.Module].[name]Factory { /* your definition */ }
*/
@Target(AnnotationTarget.FILE)
@Repeatable
annotation class GenerateKoinDefinition(
val name: String,
val type: KClass<*>,
vararg val typeArgs: KClass<*>,
val nullable: Boolean = true,
val generateSingle: Boolean = true,
val generateFactory: Boolean = true
)
@@ -0,0 +1,28 @@
package dev.inmo.micro_utils.koin
import org.koin.core.definition.Definition
import org.koin.core.definition.KoinDefinition
import org.koin.core.instance.InstanceFactory
import org.koin.core.module.Module
import org.koin.core.qualifier.Qualifier
import org.koin.dsl.binds
import kotlin.reflect.KClass
import kotlin.reflect.full.allSuperclasses
inline fun <reified T : Any> Module.factoryWithBinds(
qualifier: Qualifier? = null,
bindFilter: (KClass<*>) -> Boolean = { true },
noinline definition: Definition<T>
): KoinDefinition<*> {
return factory(qualifier, definition) binds (T::class.allSuperclasses.filter(bindFilter).toTypedArray())
}
inline fun <reified T : Any> Module.factoryWithBinds(
qualifier: String,
bindFilter: (KClass<*>) -> Boolean = { true },
noinline definition: Definition<T>
): KoinDefinition<*> {
return factory(qualifier, definition) binds (T::class.allSuperclasses.filter(bindFilter).toTypedArray())
}
@@ -0,0 +1,14 @@
package dev.inmo.micro_utils.koin
import org.koin.core.definition.Definition
import org.koin.core.definition.KoinDefinition
import org.koin.core.instance.InstanceFactory
import org.koin.core.module.Module
import kotlin.reflect.KClass
inline fun <reified T : Any> Module.factoryWithRandomQualifierAndBinds(
bindFilter: (KClass<*>) -> Boolean = { true },
noinline definition: Definition<T>
): KoinDefinition<*> {
return factoryWithBinds(RandomQualifier(), bindFilter, definition)
}
@@ -0,0 +1,31 @@
package dev.inmo.micro_utils.koin
import org.koin.core.definition.Definition
import org.koin.core.definition.KoinDefinition
import org.koin.core.instance.InstanceFactory
import org.koin.core.module.Module
import org.koin.core.qualifier.Qualifier
import org.koin.dsl.binds
import kotlin.reflect.KClass
import kotlin.reflect.full.allSuperclasses
inline fun <reified T : Any> Module.singleWithBinds(
qualifier: Qualifier? = null,
createdAtStart: Boolean = false,
bindFilter: (KClass<*>) -> Boolean = { true },
noinline definition: Definition<T>
): KoinDefinition<*> {
return single(qualifier, createdAtStart, definition) binds (T::class.allSuperclasses.filter(bindFilter).toTypedArray())
}
inline fun <reified T : Any> Module.singleWithBinds(
qualifier: String,
createdAtStart: Boolean = false,
bindFilter: (KClass<*>) -> Boolean = { true },
noinline definition: Definition<T>
): KoinDefinition<*> {
return single(qualifier, createdAtStart, definition) binds (T::class.allSuperclasses.filter(bindFilter).toTypedArray())
}
@@ -0,0 +1,15 @@
package dev.inmo.micro_utils.koin
import org.koin.core.definition.Definition
import org.koin.core.definition.KoinDefinition
import org.koin.core.instance.InstanceFactory
import org.koin.core.module.Module
import kotlin.reflect.KClass
inline fun <reified T : Any> Module.singleWithRandomQualifierAndBinds(
createdAtStart: Boolean = false,
bindFilter: (KClass<*>) -> Boolean = { true },
noinline definition: Definition<T>
): KoinDefinition<*> {
return singleWithBinds(RandomQualifier(), createdAtStart, bindFilter, definition)
}
+1
View File
@@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.koin"/>
+15
View File
@@ -15,5 +15,20 @@ kotlin {
api libs.ktor.client
}
}
androidMain {
dependsOn jvmMain
}
linuxX64Main {
dependencies {
api internalProject("micro_utils.mime_types")
}
}
mingwX64Main {
dependencies {
api internalProject("micro_utils.mime_types")
}
}
}
}
@@ -0,0 +1,13 @@
package dev.inmo.micro_utils.ktor.client
import io.ktor.client.call.body
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HttpStatusCode
suspend inline fun <reified T : Any> HttpResponse.bodyOrNull(
statusFilter: (HttpResponse) -> Boolean = { it.status == HttpStatusCode.OK }
) = takeIf(statusFilter) ?.body<T>()
suspend inline fun <reified T : Any> HttpResponse.bodyOrNullOnNoContent() = bodyOrNull<T> {
it.status != HttpStatusCode.NoContent
}
@@ -1,82 +0,0 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient
import io.ktor.client.plugins.pluginOrNull
import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.client.plugins.websocket.ws
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.websocket.Frame
import io.ktor.websocket.readBytes
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.isActive
import kotlinx.serialization.DeserializationStrategy
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
inline fun <T> HttpClient.createStandardWebsocketFlow(
url: String,
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
crossinline conversation: suspend (StandardKtorSerialInputData) -> T
): Flow<T> {
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")
val correctedUrl = url.asCorrectWebSocketUrl
return channelFlow {
do {
val reconnect = runCatchingSafely {
ws(correctedUrl, requestBuilder) {
for (received in incoming) {
when (received) {
is Frame.Binary -> send(conversation(received.data))
else -> {
close()
return@ws
}
}
}
}
checkReconnection(null)
}.getOrElse { e ->
checkReconnection(e).also {
if (!it) {
close(e)
}
}
}
} while (reconnect && isActive)
if (isActive) {
safely {
close()
}
}
}
}
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
inline fun <T> HttpClient.createStandardWebsocketFlow(
url: String,
deserializer: DeserializationStrategy<T>,
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
) = createStandardWebsocketFlow(
url,
checkReconnection,
requestBuilder
) {
serialFormat.decodeDefault(deserializer, it)
}
@@ -1,6 +1,10 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.filesize
import dev.inmo.micro_utils.ktor.common.input
import io.ktor.client.request.forms.InputProvider
expect suspend fun MPPFile.inputProvider(): InputProvider
fun MPPFile.inputProvider(): InputProvider = InputProvider(filesize) {
input()
}
@@ -19,7 +19,7 @@ import kotlinx.coroutines.isActive
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
@Warning("This feature is internal and should not be used directly. It is can be changed without any notification and warranty on compile-time or other guaranties")
inline fun <reified T : Any> openBaseWebSocketFlow(
inline fun <T : Any> openBaseWebSocketFlow(
noinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline webSocketSessionRequest: suspend SendChannel<T>.() -> Unit
): Flow<T> {
@@ -57,7 +57,7 @@ inline fun <reified T : Any> HttpClient.openWebSocketFlow(
): Flow<T> {
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")
return openBaseWebSocketFlow<T>(checkReconnection) {
return openBaseWebSocketFlow(checkReconnection) {
val block: suspend DefaultClientWebSocketSession.() -> Unit = {
while (isActive) {
send(receiveDeserialized<T>())
@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.ktor.client
typealias OnUploadCallback = suspend (uploaded: Long, count: Long) -> Unit
@@ -1,260 +0,0 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.filename
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.readBytes
import io.ktor.http.*
import io.ktor.utils.io.core.ByteReadPacket
import kotlinx.serialization.*
@Deprecated("This class will be removed soon. It is now recommended to use built-in ktor features instead")
class UnifiedRequester(
val client: HttpClient = HttpClient(),
val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) {
suspend fun <ResultType> uniget(
url: String,
resultDeserializer: DeserializationStrategy<ResultType>
): ResultType = client.uniget(url, resultDeserializer, serialFormat)
fun <T> encodeUrlQueryValue(
serializationStrategy: SerializationStrategy<T>,
value: T
) = serializationStrategy.encodeUrlQueryValue(
value,
serialFormat
)
suspend fun <BodyType, ResultType> unipost(
url: String,
bodyInfo: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>
) = client.unipost(url, bodyInfo, resultDeserializer, serialFormat)
suspend fun <ResultType> unimultipart(
url: String,
filename: String,
inputProvider: InputProvider,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {},
): ResultType = client.unimultipart(url, filename, inputProvider, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat)
suspend fun <BodyType, ResultType> unimultipart(
url: String,
filename: String,
inputProvider: InputProvider,
otherData: Pair<SerializationStrategy<BodyType>, 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: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {}
): ResultType = client.unimultipart(
url, mppFile, otherData, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat
)
fun <T> createStandardWebsocketFlow(
url: String,
checkReconnection: suspend (Throwable?) -> Boolean,
deserializer: DeserializationStrategy<T>,
requestBuilder: HttpRequestBuilder.() -> Unit = {},
) = client.createStandardWebsocketFlow(url, deserializer, checkReconnection, serialFormat, requestBuilder)
fun <T> createStandardWebsocketFlow(
url: String,
deserializer: DeserializationStrategy<T>,
requestBuilder: HttpRequestBuilder.() -> Unit = {},
) = createStandardWebsocketFlow(url, { true }, deserializer, requestBuilder)
}
@Deprecated("This property will be removed soon. It is now recommended to use built-in ktor features instead")
val defaultRequester = UnifiedRequester()
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <ResultType> HttpClient.uniget(
url: String,
resultDeserializer: DeserializationStrategy<ResultType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = get(url).let {
serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>())
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
fun <T> SerializationStrategy<T>.encodeUrlQueryValue(
value: T,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = serialFormat.encodeHex(
this,
value
)
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <BodyType, ResultType> HttpClient.unipost(
url: String,
bodyInfo: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = post(url) {
setBody(
serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
)
}.let {
serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>())
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
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(
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.body<StandardKtorSerialInputData>()) }
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
url: String,
filename: String,
otherData: Pair<SerializationStrategy<BodyType>, 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
)
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
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
)
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
url: String,
mppFile: MPPFile,
otherData: Pair<SerializationStrategy<BodyType>, 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
)
@@ -7,13 +7,5 @@ import io.ktor.client.HttpClient
expect suspend fun HttpClient.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (uploaded: Long, count: Long) -> Unit = { _, _ -> }
onUpload: OnUploadCallback = { _, _ -> }
): TemporalFileId
suspend fun UnifiedRequester.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (uploaded: Long, count: Long) -> Unit = { _, _ -> }
): TemporalFileId = client.tempUpload(
fullTempUploadDraftPath, file, onUpload
)
@@ -0,0 +1,105 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.ktor.common.LambdaInputProvider
import io.ktor.client.HttpClient
import io.ktor.http.Headers
import io.ktor.utils.io.core.Input
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.json.Json
data class UniUploadFileInfo(
val fileName: FileName,
val mimeType: String,
val inputAllocator: LambdaInputProvider
)
/**
* Will execute submitting of multipart data request
*
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
* in case you wish to pass other source of multipart binary data than regular file
*
* @see dev.inmo.micro_utils.ktor.server.handleUniUpload
*/
expect suspend fun <T> HttpClient.uniUpload(
url: String,
data: Map<String, Any>,
resultDeserializer: DeserializationStrategy<T>,
headers: Headers = Headers.Empty,
stringFormat: StringFormat = Json,
onUpload: OnUploadCallback = { _, _ -> }
): T?
/**
* Additional variant of [uniUpload] which will unify sending of some [MPPFile] with the server
*
* @see dev.inmo.micro_utils.ktor.server.uniloadMultipartFile
*/
suspend fun <T> HttpClient.uniUpload(
url: String,
file: MPPFile,
resultDeserializer: DeserializationStrategy<T>,
additionalData: Map<String, Any> = emptyMap(),
headers: Headers = Headers.Empty,
stringFormat: StringFormat = Json,
onUpload: OnUploadCallback = { _, _ -> }
): T? = uniUpload(
url,
additionalData + ("bytes" to file),
resultDeserializer,
headers,
stringFormat,
onUpload
)
/**
* Additional variant of [uniUpload] which will unify sending of some [UniUploadFileInfo] with the server
*
* @see dev.inmo.micro_utils.ktor.server.uniloadMultipartFile
*/
suspend fun <T> HttpClient.uniUpload(
url: String,
info: UniUploadFileInfo,
resultDeserializer: DeserializationStrategy<T>,
additionalData: Map<String, Any> = emptyMap(),
headers: Headers = Headers.Empty,
stringFormat: StringFormat = Json,
onUpload: OnUploadCallback = { _, _ -> }
): T? = uniUpload(
url,
additionalData + ("bytes" to info),
resultDeserializer,
headers,
stringFormat,
onUpload
)
/**
* Additional variant of [uniUpload] which will unify sending of some [UniUploadFileInfo] (built from [fileName],
* [mimeType] and [inputAllocator]) with the server
*
* @see dev.inmo.micro_utils.ktor.server.uniloadMultipartFile
*/
suspend fun <T> HttpClient.uniUpload(
url: String,
fileName: FileName,
mimeType: String,
inputAllocator: LambdaInputProvider,
resultDeserializer: DeserializationStrategy<T>,
additionalData: Map<String, Any> = emptyMap(),
headers: Headers = Headers.Empty,
stringFormat: StringFormat = Json,
onUpload: OnUploadCallback = { _, _ -> }
): T? = uniUpload(
url,
UniUploadFileInfo(fileName, mimeType, inputAllocator),
resultDeserializer,
additionalData,
headers,
stringFormat,
onUpload
)
@@ -1,11 +0,0 @@
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)
}
}
@@ -1,6 +1,8 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.micro_utils.ktor.common.TemporalFileId
import io.ktor.client.HttpClient
import kotlinx.coroutines.*
@@ -12,10 +14,11 @@ import org.w3c.xhr.XMLHttpRequest.Companion.DONE
suspend fun tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (Long, Long) -> Unit
onUpload: OnUploadCallback
): TemporalFileId {
val formData = FormData()
val answer = CompletableDeferred<TemporalFileId>(currentCoroutineContext().job)
val subscope = CoroutineScope(currentCoroutineContext().LinkedSupervisorJob())
formData.append(
"data",
@@ -25,7 +28,7 @@ suspend fun tempUpload(
val request = XMLHttpRequest()
request.responseType = XMLHttpRequestResponseType.TEXT
request.upload.onprogress = {
onUpload(it.loaded.toLong(), it.total.toLong())
subscope.launchSafelyWithoutExceptions { onUpload(it.loaded.toLong(), it.total.toLong()) }
}
request.onload = {
if (request.status == 200.toShort()) {
@@ -48,12 +51,14 @@ suspend fun tempUpload(
}
}
return answer.await()
return answer.await().also {
subscope.cancel()
}
}
actual suspend fun HttpClient.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (uploaded: Long, count: Long) -> Unit
onUpload: OnUploadCallback
): TemporalFileId = dev.inmo.micro_utils.ktor.client.tempUpload(fullTempUploadDraftPath, file, onUpload)
@@ -0,0 +1,97 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.Progress
import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import io.ktor.client.HttpClient
import io.ktor.http.Headers
import io.ktor.utils.io.core.readBytes
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.job
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.encodeToString
import org.khronos.webgl.Int8Array
import org.w3c.files.Blob
import org.w3c.xhr.FormData
import org.w3c.xhr.TEXT
import org.w3c.xhr.XMLHttpRequest
import org.w3c.xhr.XMLHttpRequestResponseType
/**
* Will execute submitting of multipart data request
*
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
* in case you wish to pass other source of multipart binary data than regular file
* @suppress
*/
actual suspend fun <T> HttpClient.uniUpload(
url: String,
data: Map<String, Any>,
resultDeserializer: DeserializationStrategy<T>,
headers: Headers,
stringFormat: StringFormat,
onUpload: OnUploadCallback
): T? {
val formData = FormData()
val answer = CompletableDeferred<T?>(currentCoroutineContext().job)
val subscope = CoroutineScope(currentCoroutineContext().LinkedSupervisorJob())
data.forEach { (k, v) ->
when (v) {
is MPPFile -> formData.append(
k,
v
)
is UniUploadFileInfo -> formData.append(
k,
Blob(arrayOf(Int8Array(v.inputAllocator().readBytes().toTypedArray()))),
v.fileName.name
)
else -> formData.append(
k,
stringFormat.encodeToString(v)
)
}
}
val request = XMLHttpRequest()
headers.forEach { s, strings ->
request.setRequestHeader(s, strings.joinToString())
}
request.responseType = XMLHttpRequestResponseType.TEXT
request.upload.onprogress = {
subscope.launchSafelyWithoutExceptions { onUpload(it.loaded.toLong(), it.total.toLong()) }
}
request.onload = {
if (request.status == 200.toShort()) {
answer.complete(
stringFormat.decodeFromString(resultDeserializer, request.responseText)
)
} else {
answer.completeExceptionally(Exception("Something went wrong: $it"))
}
}
request.onerror = {
answer.completeExceptionally(Exception("Something went wrong: $it"))
}
request.open("POST", url, true)
request.send(formData)
answer.invokeOnCompletion {
runCatching {
if (request.readyState != XMLHttpRequest.DONE) {
request.abort()
}
}
}
return answer.await().also {
subscope.cancel()
}
}
@@ -4,6 +4,6 @@ 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()) {
fun MPPFile.inputProviderSync(): InputProvider = InputProvider(length()) {
inputStream().asInput()
}
@@ -18,7 +18,7 @@ internal val MPPFile.mimeType: String
actual suspend fun HttpClient.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (Long, Long) -> Unit
onUpload: OnUploadCallback
): TemporalFileId {
val inputProvider = file.inputProvider()
val fileId = submitFormWithBinaryData(
@@ -0,0 +1,107 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.Progress
import io.ktor.client.HttpClient
import io.ktor.client.engine.mergeHeaders
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.forms.InputProvider
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitForm
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.request.headers
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.Parameters
import io.ktor.http.content.PartData
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.encodeToString
import kotlinx.serialization.serializer
import java.io.File
/**
* Will execute submitting of multipart data request
*
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
* in case you wish to pass other source of multipart binary data than regular file
* @suppress
*/
@OptIn(InternalSerializationApi::class)
actual suspend fun <T> HttpClient.uniUpload(
url: String,
data: Map<String, Any>,
resultDeserializer: DeserializationStrategy<T>,
headers: Headers,
stringFormat: StringFormat,
onUpload: OnUploadCallback
): T? {
val withBinary = data.values.any { it is File || it is UniUploadFileInfo }
val formData = formData {
for (k in data.keys) {
val v = data[k] ?: continue
when (v) {
is File -> append(
k,
v.inputProviderSync(),
Headers.build {
append(HttpHeaders.ContentType, v.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${v.name}\"")
}
)
is UniUploadFileInfo -> append(
k,
InputProvider(block = v.inputAllocator),
Headers.build {
append(HttpHeaders.ContentType, v.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${v.fileName.name}\"")
}
)
else -> append(
k,
stringFormat.encodeToString(v::class.serializer() as SerializationStrategy<in Any>, v)
)
}
}
}
val requestBuilder: HttpRequestBuilder.() -> Unit = {
headers {
appendAll(headers)
}
onUpload { bytesSentTotal, contentLength ->
onUpload(bytesSentTotal, contentLength)
}
}
val response = if (withBinary) {
submitFormWithBinaryData(
url,
formData,
block = requestBuilder
)
} else {
submitForm(
url,
Parameters.build {
for (it in formData) {
val formItem = (it as PartData.FormItem)
append(it.name!!, it.value)
}
},
block = requestBuilder
)
}
return if (response.status == HttpStatusCode.OK) {
stringFormat.decodeFromString(resultDeserializer, response.bodyAsText())
} else {
null
}
}
@@ -0,0 +1,40 @@
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.TemporalFileId
import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny
import io.ktor.client.HttpClient
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
internal val MPPFile.mimeType: String
get() = getMimeTypeOrAny(filename.extension).raw
actual suspend fun HttpClient.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: OnUploadCallback
): TemporalFileId {
val inputProvider = file.inputProvider()
val fileId = submitFormWithBinaryData(
fullTempUploadDraftPath,
formData = formData {
append(
"data",
inputProvider,
Headers.build {
append(HttpHeaders.ContentType, file.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${file.filename.string}\"")
}
)
}
) {
onUpload(onUpload)
}.bodyAsText()
return TemporalFileId(fileId)
}
@@ -0,0 +1,107 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.Progress
import io.ktor.client.HttpClient
import io.ktor.client.engine.mergeHeaders
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.forms.InputProvider
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitForm
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.request.headers
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.Parameters
import io.ktor.http.content.PartData
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.encodeToString
import kotlinx.serialization.serializer
/**
* Will execute submitting of multipart data request
*
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
* in case you wish to pass other source of multipart binary data than regular file
* @suppress
*/
@OptIn(InternalSerializationApi::class)
actual suspend fun <T> HttpClient.uniUpload(
url: String,
data: Map<String, Any>,
resultDeserializer: DeserializationStrategy<T>,
headers: Headers,
stringFormat: StringFormat,
onUpload: OnUploadCallback
): T? {
val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo }
val formData = formData {
for (k in data.keys) {
val v = data[k] ?: continue
when (v) {
is MPPFile -> append(
k,
v.inputProvider(),
Headers.build {
append(HttpHeaders.ContentType, v.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${v.name}\"")
}
)
is UniUploadFileInfo -> append(
k,
InputProvider(block = v.inputAllocator),
Headers.build {
append(HttpHeaders.ContentType, v.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${v.fileName.name}\"")
}
)
else -> append(
k,
stringFormat.encodeToString(v::class.serializer() as SerializationStrategy<in Any>, v)
)
}
}
}
val requestBuilder: HttpRequestBuilder.() -> Unit = {
headers {
appendAll(headers)
}
onUpload { bytesSentTotal, contentLength ->
onUpload(bytesSentTotal, contentLength)
}
}
val response = if (withBinary) {
submitFormWithBinaryData(
url,
formData,
block = requestBuilder
)
} else {
submitForm(
url,
Parameters.build {
for (it in formData) {
val formItem = (it as PartData.FormItem)
append(it.name!!, it.value)
}
},
block = requestBuilder
)
}
return if (response.status == HttpStatusCode.OK) {
stringFormat.decodeFromString(resultDeserializer, response.bodyAsText())
} else {
null
}
}
@@ -0,0 +1,40 @@
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.TemporalFileId
import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny
import io.ktor.client.HttpClient
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
internal val MPPFile.mimeType: String
get() = getMimeTypeOrAny(filename.extension).raw
actual suspend fun HttpClient.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: OnUploadCallback
): TemporalFileId {
val inputProvider = file.inputProvider()
val fileId = submitFormWithBinaryData(
fullTempUploadDraftPath,
formData = formData {
append(
"data",
inputProvider,
Headers.build {
append(HttpHeaders.ContentType, file.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${file.filename.string}\"")
}
)
}
) {
onUpload(onUpload)
}.bodyAsText()
return TemporalFileId(fileId)
}
@@ -0,0 +1,107 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.Progress
import io.ktor.client.HttpClient
import io.ktor.client.engine.mergeHeaders
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.forms.InputProvider
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitForm
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.request.headers
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.Parameters
import io.ktor.http.content.PartData
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.encodeToString
import kotlinx.serialization.serializer
/**
* Will execute submitting of multipart data request
*
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
* in case you wish to pass other source of multipart binary data than regular file
* @suppress
*/
@OptIn(InternalSerializationApi::class)
actual suspend fun <T> HttpClient.uniUpload(
url: String,
data: Map<String, Any>,
resultDeserializer: DeserializationStrategy<T>,
headers: Headers,
stringFormat: StringFormat,
onUpload: OnUploadCallback
): T? {
val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo }
val formData = formData {
for (k in data.keys) {
val v = data[k] ?: continue
when (v) {
is MPPFile -> append(
k,
v.inputProvider(),
Headers.build {
append(HttpHeaders.ContentType, v.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${v.name}\"")
}
)
is UniUploadFileInfo -> append(
k,
InputProvider(block = v.inputAllocator),
Headers.build {
append(HttpHeaders.ContentType, v.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${v.fileName.name}\"")
}
)
else -> append(
k,
stringFormat.encodeToString(v::class.serializer() as SerializationStrategy<in Any>, v)
)
}
}
}
val requestBuilder: HttpRequestBuilder.() -> Unit = {
headers {
appendAll(headers)
}
onUpload { bytesSentTotal, contentLength ->
onUpload(bytesSentTotal, contentLength)
}
}
val response = if (withBinary) {
submitFormWithBinaryData(
url,
formData,
block = requestBuilder
)
} else {
submitForm(
url,
Parameters.build {
for (it in formData) {
val formItem = (it as PartData.FormItem)
append(it.name!!, it.value)
}
},
block = requestBuilder
)
}
return if (response.status == HttpStatusCode.OK) {
stringFormat.decodeFromString(resultDeserializer, response.bodyAsText())
} else {
null
}
}
+3
View File
@@ -17,5 +17,8 @@ kotlin {
api libs.ktor.io
}
}
androidMain {
dependsOn jvmMain
}
}
}
@@ -0,0 +1,5 @@
package dev.inmo.micro_utils.ktor.common
import io.ktor.utils.io.core.Input
typealias LambdaInputProvider = () -> Input
@@ -0,0 +1,23 @@
package dev.inmo.micro_utils.ktor.common
import io.ktor.utils.io.core.Input
import io.ktor.utils.io.core.copyTo
import io.ktor.utils.io.streams.asOutput
import java.io.File
import java.io.InputStream
import java.util.UUID
fun Input.downloadToTempFile(
fileName: String = UUID.randomUUID().toString(),
fileExtension: String? = ".temp",
folder: File? = null
) = File.createTempFile(
fileName,
fileExtension,
folder
).apply {
outputStream().use {
copyTo(it.asOutput())
}
deleteOnExit()
}
Loaded 100 of 274 files, more files were not shown because too many files have changed in this diff. Show more