binary command protocol minimalistic test

This commit is contained in:
Sergey Chernov 2022-09-07 11:45:06 +03:00
parent 5b94ab1bfd
commit db7a85fb7b
11 changed files with 548 additions and 21 deletions

View File

@ -38,13 +38,20 @@ kotlin {
dependencies { dependencies {
implementation("io.ktor:ktor-client-core:$ktor_version") implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-websockets:$ktor_version") implementation("io.ktor:ktor-client-websockets:$ktor_version")
api("net.sergeych:boss-serialization-mp:0.1.3-SNAPSHOT") api("net.sergeych:unikrypto:1.2.0-SNAPSHOT")
implementation("net.sergeych:mp_stools:[1.3.2-SNAPSHOT,)") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3")
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-websockets:$ktor_version")
api("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
api("net.sergeych:boss-serialization-mp:[0.1.3-SNAPSHOT,)")
api("net.sergeych:mp_stools:1.2.3-SNAPSHOT")
} }
} }
val commonTest by getting { val commonTest by getting {
dependencies { dependencies {
implementation(kotlin("test")) implementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
} }
} }
val jvmMain by getting { val jvmMain by getting {

View File

@ -316,6 +316,13 @@ ansi-regex@^5.0.1:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
ansi-styles@^4.0.0, ansi-styles@^4.1.0: ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0" version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
@ -341,6 +348,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
base64id@2.0.0, base64id@~2.0.0: base64id@2.0.0, base64id@~2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
@ -351,6 +363,11 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
bn.js@^4.0.0, bn.js@^4.1.0:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
body-parser@^1.19.0: body-parser@^1.19.0:
version "1.20.0" version "1.20.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5"
@ -391,6 +408,11 @@ braces@^3.0.2, braces@~3.0.2:
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.0.1"
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==
browser-stdout@1.3.1: browser-stdout@1.3.1:
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
@ -411,6 +433,14 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer@^5.4.2:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.1.13"
bytes@3.1.2: bytes@3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
@ -434,6 +464,15 @@ caniuse-lite@^1.0.30001370:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001383.tgz#aecf317ccd940690725ae3ae4f28293c5fb8050e" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001383.tgz#aecf317ccd940690725ae3ae4f28293c5fb8050e"
integrity sha512-swMpEoTp5vDoGBZsYZX7L7nXHe6dsHxi9o6/LKf/f0LukVtnrxly5GVb/fWdCDTqi/yw6Km6tiJ0pmBacm0gbg== integrity sha512-swMpEoTp5vDoGBZsYZX7L7nXHe6dsHxi9o6/LKf/f0LukVtnrxly5GVb/fWdCDTqi/yw6Km6tiJ0pmBacm0gbg==
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^4.1.0: chalk@^4.1.0:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
@ -480,6 +519,13 @@ clone-deep@^4.0.1:
kind-of "^6.0.2" kind-of "^6.0.2"
shallow-clone "^3.0.0" shallow-clone "^3.0.0"
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-convert@^2.0.1: color-convert@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
@ -487,6 +533,11 @@ color-convert@^2.0.1:
dependencies: dependencies:
color-name "~1.1.4" color-name "~1.1.4"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
color-name@~1.1.4: color-name@~1.1.4:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
@ -622,6 +673,15 @@ diff@5.0.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
diffie-hellman@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
dependencies:
bn.js "^4.1.0"
miller-rabin "^4.0.0"
randombytes "^2.0.0"
dom-serialize@^2.2.1: dom-serialize@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
@ -719,6 +779,11 @@ escape-string-regexp@4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
eslint-scope@5.1.1: eslint-scope@5.1.1:
version "5.1.1" version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@ -779,6 +844,11 @@ fastest-levenshtein@^1.0.12:
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
fastestsmallesttextencoderdecoder@^1.0.14:
version "1.0.22"
resolved "https://registry.yarnpkg.com/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz#59b47e7b965f45258629cc6c127bf783281c5e93"
integrity sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==
fill-range@^7.0.1: fill-range@^7.0.1:
version "7.0.1" version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@ -859,6 +929,14 @@ function-bind@^1.1.1:
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
gently-copy@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/gently-copy/-/gently-copy-3.2.0.tgz#6f3b9061c00db6d9bed734b728d93358d1137105"
integrity sha512-IBLU4rCffg0Dvq3/7KyiPionCCdEdKnyfe94c00C8+VbgzIS2J9L2jHdLchG9sn8lDqBGzbvfuYVZB/ZlffS7g==
dependencies:
chalk "^2.4.2"
shelljs "^0.8.3"
get-caller-file@^2.0.5: get-caller-file@^2.0.5:
version "2.0.5" version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@ -897,7 +975,7 @@ glob@7.2.0:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.1.3, glob@^7.1.7: glob@^7.0.0, glob@^7.1.3, glob@^7.1.7:
version "7.2.3" version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@ -919,6 +997,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4,
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
has-flag@^4.0.0: has-flag@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
@ -980,6 +1063,11 @@ icss-utils@^5.0.0, icss-utils@^5.1.0:
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
ieee754@^1.1.13:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
import-local@^3.0.2: import-local@^3.0.2:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
@ -1001,6 +1089,11 @@ inherits@2, inherits@2.0.4:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
interpret@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
interpret@^2.2.0: interpret@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
@ -1090,6 +1183,11 @@ js-yaml@4.1.0:
dependencies: dependencies:
argparse "^2.0.1" argparse "^2.0.1"
jsbn@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
json-parse-even-better-errors@^2.3.1: json-parse-even-better-errors@^2.3.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
@ -1232,6 +1330,14 @@ merge-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
dependencies:
bn.js "^4.0.0"
brorand "^1.0.1"
mime-db@1.52.0: mime-db@1.52.0:
version "1.52.0" version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
@ -1528,7 +1634,7 @@ qs@6.10.3:
dependencies: dependencies:
side-channel "^1.0.4" side-channel "^1.0.4"
randombytes@^2.1.0: randombytes@^2.0.0, randombytes@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
@ -1557,6 +1663,13 @@ readdirp@~3.6.0:
dependencies: dependencies:
picomatch "^2.2.1" picomatch "^2.2.1"
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==
dependencies:
resolve "^1.1.6"
rechoir@^0.7.0: rechoir@^0.7.0:
version "0.7.1" version "0.7.1"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
@ -1586,7 +1699,7 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve@^1.9.0: resolve@^1.1.6, resolve@^1.9.0:
version "1.22.1" version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
@ -1664,6 +1777,15 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
shelljs@^0.8.3:
version "0.8.5"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
dependencies:
glob "^7.0.0"
interpret "^1.0.0"
rechoir "^0.6.2"
side-channel@^1.0.4: side-channel@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@ -1778,6 +1900,13 @@ supports-color@8.1.1, supports-color@^8.0.0:
dependencies: dependencies:
has-flag "^4.0.0" has-flag "^4.0.0"
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
supports-color@^7.1.0: supports-color@^7.1.0:
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
@ -1858,6 +1987,18 @@ ua-parser-js@^0.7.30:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
unicrypto@1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/unicrypto/-/unicrypto-1.14.0.tgz#eee5f2d88d33bb6ba774c8395edf3f4e2a4a68dc"
integrity sha512-NNaMM2Has6Dzk0OAxhR/OfocTKPgA4TbseKqXZ2A7Kb1gcBRN+He7wuxO6QdDOP7tE5SuXVf9C8R9xeswi/0MQ==
dependencies:
buffer "^5.4.2"
diffie-hellman "^5.0.3"
fastestsmallesttextencoderdecoder "^1.0.14"
gently-copy "^3.2.0"
jsbn "^1.1.0"
randombytes "^2.1.0"
universalify@^0.1.0: universalify@^0.1.0:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"

View File

@ -0,0 +1,165 @@
package channel
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.json.Json
import net.sergeych.boss_serialization.BossDecoder
import net.sergeych.boss_serialization_mp.BossEncoder
import net.sergeych.boss_serialization_mp.decodeBoss
import net.sergeych.cloudoc.api.ApiError.BAD_RESPONSE_PACKAGE
import net.sergeych.cloudoc.api.ApiError.UNKNOWN_ERROR
import net.sergeych.cloudoc.api.ApiException
import net.sergeych.cloudoc.api.Package
import net.sergeych.mp_logger.LogTag
import net.sergeych.mp_logger.debug
import net.sergeych.mp_logger.exception
import net.sergeych.mp_logger.warning
import net.sergeych.mptools.toDump
/**
* Create adapter, an interface to provide local API commands and invoke remote API commands
* asynchronously and concurrently. To implement adapter over some protocol you need:
*
* - create some class T that will hold the "state" of the API, e.g. session. Use `Unit` for stateless
* - create an CommandHost<T> class and fill it with commands to be executed by a local party (could be empty).
* - provide a method that will transmit binary frames to a remote
* - for any incoming binary frame call [Adapter.receiveFrame].
*
* Here is the sample of short-circuit pair of adapters:
*
* ```
* val ch12 = Channel<ByteArray>()
* val ch21 = Channel<ByteArray>()
*
* val api1 = Api1()
* val api2 = Api2()
*
* // this interface is provided by api1 locally
* api1.on(api1.foo) {
* it + "foo"
* }
* // and that by api2 locally
* api2.on(api2.bar) {
* it + "bar"
* }
*
* // respective adapters that send to a channel:
* val a1 = Adapter(Unit,api1) { ch12.send(it) }
* val a2 = Adapter(Unit,api2) { ch21.send(it) }
*
* // pumps to load frames from the respecive channel and pass them to the adapter:
* launch { for( b in ch12) a2.receiveFrame(b) }
* launch { for( b in ch21) a1.receiveFrame(b) }
*
* // note that adapter `a1` is exepcted to provide `Api2` and vice versa:
* assertEquals("123bar", a1.invokeCommand(api2.bar, "123"))
* assertEquals("321foo", a2.invokeCommand(api1.foo, "321"))
*
* ch12.cancel() // to close channel pump
* ch21.cancel()
* ```
*
* See [CommandHost] class documentation to learn how to declare API interfaces in a compile time type safe
* manner.
*
* @param instance any instance that represent the state of the interface. Could be `Unit` for stateless.
* @param commandHost the Api __this adapter provides to a remote__. It differs from the interface expected on the
* remote side.
* @param sendEncoded a method that performs actual sending of the packed binary frame to the remote side
*/
open class Adapter<T>(
private val instance: T,
private val commandHost: CommandHost<T>,
private val sendEncoded: suspend (data: ByteArray) -> Unit,
) : LogTag("ADPTR") {
private val completions = mutableMapOf<Int, CompletableDeferred<ByteArray>>()
private var lastId = 1
private val access = Mutex()
/**
* Call the remote party for a type command. See [CommandHost] on how to declare and implement
* such commands in parsec3. Suspends until receiving answer from a remote party.
*
* @param ca command descriptor, provided by [CommandHost.command], usually, it should be a val in the
* [CommandHost] or derived instance.
* @param args command specific args of any serializable type
* @return value from remote partm any serializable type.
*/
@Suppress("UNCHECKED_CAST")
suspend fun <A, R> invokeCommand(ca: CommandDescriptor<T, A, R>, args: A = Unit as A): R {
var myId = -1
return CompletableDeferred<ByteArray>().also { dr ->
sendPackage(
access.withLock {
debug { "calling $lastId:${ca.name}($args)" }
completions[lastId] = dr
myId = lastId
Package.Command(lastId++, ca.name, BossEncoder.encode(ca.ass, args))
}
)
}.await().let {
debug { "result $myId:$it" }
BossDecoder.decodeFrom(ca.rss, it)
}
}
private suspend fun processIncomingPackage(pe: Package) {
when (pe) {
is Package.Command -> {
try {
val handler = commandHost.handler(pe.name)
val result = handler.invoke(instance, pe.args)
sendPackage(
Package.Response(
pe.id, result
)
)
} catch (ae: ApiException) {
sendPackage(Package.Response(pe.id, null, ae.code, ae.text))
} catch (ex: Throwable) {
ex.printStackTrace()
sendPackage(Package.Response(pe.id, null, UNKNOWN_ERROR, ex.toString()))
}
}
is Package.Response -> {
val dr = access.withLock { completions.remove(pe.toId) }
if (dr == null)
warning { "response to unregistered toId: ${pe.toId}, ignoring" }
else {
if (pe.result != null)
dr.complete(pe.result)
else
dr.completeExceptionally(
pe.errorCode?.let { ApiException(it, pe.errorText) }
?: ApiException(BAD_RESPONSE_PACKAGE)
)
}
}
}
}
private suspend fun sendPackage(pe: Package) {
sendEncoded(BossEncoder.encode(pe))
}
/**
* Provide an incoming frame to the adapter. Consumer software receives binary blocks from whatever
* protocol it uses (say, UDP) and feed them to this method.
*/
suspend fun receiveFrame(data: ByteArray) {
try {
processIncomingPackage(data.decodeBoss())
} catch (x: Exception) {
exception { "unexpected error processing frame: \n${data.toDump()}" to x }
}
}
companion object {
val format = Json { prettyPrint = true }
}
}

View File

@ -0,0 +1,22 @@
package net.sergeych.parsec3
import channel.CommandDescriptor
import kotlin.reflect.KProperty
import kotlin.reflect.KType
/**
* Delegate to generate proper [CommandDescriptor] instances. See [CommandHost] for usage sample, and
* [Adapter] form more information/
*/
class AdapterDelegate<I, A, R>(
val overrideName: String? = null,
val ass: KType,
val rss: KType,
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): CommandDescriptor<I, A, R> {
return CommandDescriptor(
overrideName ?: property.name,
ass, rss
)
}
}

View File

@ -0,0 +1,20 @@
package channel
import kotlin.reflect.KType
class CommandDescriptor<I, A, R>(
val name: String,
val ass: KType,
val rss: KType,
) {
suspend operator fun invoke(adapter: Adapter<I>, args: A): R =
adapter.invokeCommand(this, args)
@Suppress("UNCHECKED_CAST")
suspend operator fun invoke(adapter: Adapter<I>): R = adapter.invokeCommand(this,Unit as A)
operator fun invoke(commandHost: CommandHost<I>, block: suspend I.(A)->R) {
commandHost.on(this, block)
}
}

View File

@ -0,0 +1,62 @@
package channel
import net.sergeych.boss_serialization.BossDecoder
import net.sergeych.boss_serialization_mp.BossEncoder
import net.sergeych.cloudoc.api.ApiError
import net.sergeych.parsec3.AdapterDelegate
import kotlin.reflect.typeOf
/**
* The class that provides parsec3 commands for remote callers. Could be used as is oar as a base
* class. For more information see [Adapter] class. The basic usage pattern is:
* ~~~
* class Api1: CommandHost<Unit>() {
* // create command `foo` that takes a string argument and
* // returns a string:
* val foo by command<String,String>()
* }
* ~~~
*
* Then somewhere else (usually before initializing network connection) provide implementation for
* the declared commands:
* ~~~
* val api1 = Api1()
* val api2 = Api2()
* api1.on(api1.foo) {
* it + "foo"
* }
* ~~~
*
* The good strategy is to create _one Api implementation_, if need using session type [T] to provide
* state-aware behavior, and share it when creating adapters on, say, incoming connections.
*
* @param T the type of the `state` instance used to hold state, use `Unit` for stateless interfaces
*/
open class CommandHost<T> {
private val handlers = mutableMapOf<String, suspend T.(ByteArray) -> ByteArray>()
/**
* Provide implementation for a specific command in type-safe compile-time checked manner. the command
* should be declared with [command] invocation.
*/
fun <A, R> on(ca: CommandDescriptor<T, A, R>, block: suspend T.(A) -> R) {
handlers[ca.name] = {args ->
val decodedArgs = BossDecoder.decodeFrom<A>(ca.ass, args)
BossEncoder.encode(ca.rss, block(decodedArgs))
}
}
fun handler(name: String) = handlers.get(name) ?: ApiError.NOT_FOUND.raise("command not found")
/**
* Provide a command delegate that creates type-safe command descriptor containint command name and
* types of it arguments and return value.
*/
inline fun <reified A, reified R> command(name: String? = null): AdapterDelegate<T, A, R> {
return AdapterDelegate(
name,
typeOf<A>(),
typeOf<R>()
)
}
}

View File

@ -2,17 +2,23 @@ package net.sergeych.parsec3
import net.sergeych.boss_serialization.BossDecoder import net.sergeych.boss_serialization.BossDecoder
import net.sergeych.boss_serialization_mp.BossEncoder import net.sergeych.boss_serialization_mp.BossEncoder
import net.sergeych.boss_serialization_mp.decodeBoss
import net.sergeych.boss_serialization_mp.stored
import net.sergeych.mptools.toDump import net.sergeych.mptools.toDump
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KType import kotlin.reflect.KType
import kotlin.reflect.typeOf import kotlin.reflect.typeOf
/**
* Generic storage of binary content. PArsec uses boss encoding to store everything in it
* in te convenient way. See [KVStorage.stored] delegate. The [MemoryKVStorage] allows to
* store values in-memory and then connect some permanent stprage to it in a transparent way.
*/
interface KVStorage { interface KVStorage {
operator fun get(key: String): ByteArray? operator fun get(key: String): ByteArray?
operator fun set(key: String, value: ByteArray?) operator fun set(key: String, value: ByteArray?)
operator fun contains(key: String): Boolean operator fun contains(key: String): Boolean
val keys: Collection<String> val keys: Collection<String>
fun clear() { fun clear() {
@ -30,13 +36,9 @@ interface KVStorage {
inline operator fun <reified T> KVStorage.invoke(defaultValue: T,overrideName: String? = null) = inline operator fun <reified T> KVStorage.invoke(defaultValue: T,overrideName: String? = null) =
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName) KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName)
inline fun <reified T> KVStorage.storage(defaultValue: T,overrideName: String? = null) = inline fun <reified T> KVStorage.stored(defaultValue: T, overrideName: String? = null) =
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName) KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName)
//inline fun <reified T> storage(s: KVStorage, defaultValue: T,overrideName: String? = null) =
// KVStorageDelegate<T>(s, typeOf<T>(), defaultValue, overrideName)
//
class KVStorageDelegate<T>( class KVStorageDelegate<T>(
private val storage: KVStorage, private val storage: KVStorage,
private val type: KType, private val type: KType,
@ -70,7 +72,9 @@ class KVStorageDelegate<T>(
} }
} }
/**
* Memory storage allows to connect existing in-memory content to some permanent storage on the fly
*/
class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage { class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage {
// is used when connected: // is used when connected:

View File

@ -0,0 +1,28 @@
package net.sergeych.cloudoc.api
import kotlinx.serialization.Serializable
@Serializable
sealed class Package {
@Serializable
data class Command(val id: Int, val name: String, val args: ByteArray) : Package()
@Serializable
data class Response(
val toId: Int,
val result: ByteArray? = null,
val errorCode: ApiError? = null,
val errorText: String? = null,
) : Package() {
init {
if (result == null && errorCode == null)
throw IllegalArgumentException("either result or errorCode must not be null")
}
}
// @Serializable
// data class Push(val payload: PushPayload) : Package()
}

View File

@ -0,0 +1,27 @@
package net.sergeych.cloudoc.api
enum class ApiError {
UNKNOWN_ERROR,
INTERNAL_ERROR,
BAD_RESPONSE_PACKAGE,
NOT_FOUND,
BAD_LOGIN,
OBJECT_ALREADY_EXISTS,
ACCESS_FORBIDDEN,
ILLEGAL_STATE,
NOT_LOGGED_IN,
BAD_PARAMETER,
EMAIL_IN_USE,
NAME_IN_USE;
fun raise(text: String? = null): Nothing {
throw ApiException(this, text ?: defaultText())
}
fun defaultText(): String = name.lowercase().replace('_', ' ')
}
class ApiException(val code: ApiError, _text: String? = null, cause: Throwable? = null):
Exception(_text ?: code.defaultText(), cause) {
val text by lazy { _text ?: code.defaultText() }
}

View File

@ -0,0 +1,49 @@
package parsec3
import channel.Adapter
import channel.CommandHost
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
internal class AdapterTest {
class Api1: CommandHost<Unit>() {
// create command `foo` that takes a string argument and
// returns a string:
val foo by command<String,String>()
}
class Api2: CommandHost<Unit>() {
val bar by command<String,String>()
}
@Test
fun interconnect() = runTest {
val ch12 = Channel<ByteArray>()
val ch21 = Channel<ByteArray>()
val api1 = Api1()
val api2 = Api2()
api1.on(api1.foo) {
it + "foo"
}
api2.on(api2.bar) {
it + "bar"
}
val a1 = Adapter(Unit,api1) { ch12.send(it) }
val a2 = Adapter(Unit,api2) { ch21.send(it) }
launch { for( b in ch12) a2.receiveFrame(b) }
launch { for( b in ch21) a1.receiveFrame(b) }
assertEquals("123bar", a1.invokeCommand(api2.bar, "123"))
assertEquals("321foo", a2.invokeCommand(api1.foo, "321"))
ch12.cancel()
ch21.cancel()
}
}

View File

@ -1,26 +1,28 @@
package net.sergeych.parsec3 package parsec3
import net.sergeych.mptools.toDump import net.sergeych.mptools.toDump
import kotlin.test.* import net.sergeych.parsec3.MemoryKVStorage
import net.sergeych.parsec3.stored
import org.junit.jupiter.api.Assertions.* import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
internal class MemoryKVStorageTest { internal class MemoryKVStorageTest {
@Test @Test
fun connectToStorage() { fun testStorage() {
val s = defaultNamedStorage("test11") val s = MemoryKVStorage()
s.clear() s.clear()
println(s) println(s)
println(s["foo"]) println(s["foo"])
s["foo"] = byteArrayOf(1,2,3) s["foo"] = byteArrayOf(1,2,3)
var bar: String? by s<String?>(null) var bar: String? by s.stored(null)
assertNull(bar) assertNull(bar)
bar = "foo" bar = "foo"
println(s.get("bar")?.toDump()) println(s.get("bar")?.toDump())
assertEquals("foo", bar) assertEquals("foo", bar)
var foo: String by s("", "bar") var foo: String by s.stored("", "bar")
assertEquals("foo", foo) assertEquals("foo", foo)
// //
// var i: Int? by s(defaultValue = 17) // var i: Int? by s(defaultValue = 17)