diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index fc601496..acd6d294 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -47,6 +47,10 @@ jobs: fetch-depth: 0 - name: Install Dart uses: dart-lang/setup-dart@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Setup pnpm uses: pnpm/action-setup@v2 with: @@ -78,6 +82,10 @@ jobs: fetch-depth: 0 - name: Install Dart uses: dart-lang/setup-dart@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Setup pnpm uses: pnpm/action-setup@v2 with: diff --git a/.gitignore b/.gitignore index f898eef1..765a30aa 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ dist tmp /out-tsc **/.output +**/target # dependencies node_modules diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b43a12a6..8d3a12ba 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,7 +4,9 @@ "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "mathiasfrohlich.kotlin", + "dart-code.dart-code", "dart-code.flutter", - "dart-code.dart-code" + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 40b26dfa..e2bf68a9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,6 +21,7 @@ "Arri", "arrirpc", "autoroutes", + "chrono", "citty", "codegen", "Consola", @@ -32,6 +33,7 @@ "nitropack", "ofetch", "pathe", + "reqwest", "postversion", "pubspec", "scule", @@ -43,6 +45,14 @@ "vals", "websockets" ], + "rust-analyzer.linkedProjects": [ + "languages/rust/rust-client/Cargo.toml", + "languages/rust/rust-codegen-reference/Cargo.toml", + "tests/clients/rust/Cargo.toml" + ], + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer" + }, "eslint.validate": ["json"], "java.compile.nullAnalysis.mode": "automatic", "eslint.experimental.useFlatConfig": true diff --git a/eslint.config.js b/eslint.config.js index b298c07a..72995cec 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -19,7 +19,7 @@ const ignoreFiles = [ "**/**/dist/*.d.ts", "**/*.dart", "**/*.kt", - "**/*.rust", + "**/*.rs", "**/*.zig", "**/*.swift", "**/*.go", diff --git a/internal/scripts/publish-all.sh b/internal/scripts/publish-all.sh index 526b529a..d47db932 100644 --- a/internal/scripts/publish-all.sh +++ b/internal/scripts/publish-all.sh @@ -6,3 +6,6 @@ pnpm publish -r --otp $otp echo "\n\nPublishing Dart client..." pnpm nx publish dart-client + +echo "\n\nPublishing Rust client..." +pnpm nx publish rust-client \ No newline at end of file diff --git a/internal/scripts/version-sync.ts b/internal/scripts/version-sync.ts index bbfadce8..03b8b9b1 100644 --- a/internal/scripts/version-sync.ts +++ b/internal/scripts/version-sync.ts @@ -13,6 +13,7 @@ async function main() { if (!version) { throw Error("No version in package.json file"); } + // DART packages const pubspecParts = readFileSync( "languages/dart/dart-client/pubspec.yaml", { encoding: "utf-8" }, @@ -27,6 +28,7 @@ async function main() { "languages/dart/dart-client/pubspec.yaml", pubspecParts.join("\n"), ); + // PACKAGE JSONS const childPackageJsons = await globby([ "languages/**/package.json", "tooling/**/package.json", @@ -47,9 +49,29 @@ async function main() { ); }); await Promise.all(tasks); - // sync test clients - execSync("nx run-many -t pub -- get"); + execSync("nx run-many -t pub -- get", { + stdio: "inherit", + }); + + // RUST client + const cargoTomlParts = readFileSync( + "languages/rust/rust-client/cargo.toml", + { encoding: "utf-8" }, + ).split("\n"); + for (let i = 0; i < cargoTomlParts.length; i++) { + const line = cargoTomlParts[i]; + if (line?.startsWith("version = ")) { + cargoTomlParts[i] = `version = "${version}"`; + } + } + writeFileSync( + "languages/rust/rust-client/cargo.toml", + cargoTomlParts.join("\n"), + ); + execSync(`nx run-many -t cargo -- check`, { + stdio: "inherit", + }); } void main(); diff --git a/languages/rust/rust-client/Cargo.lock b/languages/rust/rust-client/Cargo.lock new file mode 100644 index 00000000..19c349c7 --- /dev/null +++ b/languages/rust/rust-client/Cargo.lock @@ -0,0 +1,1256 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arri_client" +version = "0.51.2" +dependencies = [ + "async-trait", + "chrono", + "regex", + "reqwest", + "serde_json", +] + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b918671670962b48bc23753aef0c51d072dca6f52f01f800854ada6ddb7f7d3" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.0", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/languages/rust/rust-client/Cargo.toml b/languages/rust/rust-client/Cargo.toml new file mode 100644 index 00000000..8a34af23 --- /dev/null +++ b/languages/rust/rust-client/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "arri_client" +version = "0.51.2" +edition = "2021" +description = "Client library needed to use rust code generated by the Arri CLI" +license = "MIT" +publish = true +homepage = "https://github.com/modiimedia/arri" +repository = "https://github.com/modiimedia/arri" +keywords = ["arrirpc", "arri", "http", "rpc"] +categories = ["parsing", "web-programming::http-client"] +include = ["/src"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-trait = "0.1.77" +chrono = "0.4.34" +regex = "1.10.4" +reqwest = {version = "0.11.24", features = ["stream"]} +serde_json = "1.0" diff --git a/languages/rust/rust-client/README.md b/languages/rust/rust-client/README.md new file mode 100644 index 00000000..9a3a8b3c --- /dev/null +++ b/languages/rust/rust-client/README.md @@ -0,0 +1,3 @@ +# Rust Arri Client + +This library is needed to use the Rust code generated by [Arri RPC](https://github.com/modiimedia/arri) diff --git a/languages/rust/rust-client/project.json b/languages/rust/rust-client/project.json new file mode 100644 index 00000000..d692bdfb --- /dev/null +++ b/languages/rust/rust-client/project.json @@ -0,0 +1,36 @@ +{ + "name": "rust-client", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "languages/rust/rust-client/src", + "projectType": "library", + "targets": { + "cargo": { + "executor": "nx:run-commands", + "options": { + "command": "cargo", + "cwd": "languages/rust/rust-client" + } + }, + "test": { + "executor": "nx:run-commands", + "inputs": [ + "{projectRoot}/src", + "{projectRoot}/Cargo.lock", + "{projectRoot}/Cargo.toml" + ], + "outputs": ["{projectRoot}/target"], + "options": { + "command": "cargo test", + "cwd": "languages/rust/rust-client" + } + }, + "publish": { + "executor": "nx:run-commands", + "options": { + "command": "cargo publish", + "cwd": "languages/rust/rust-client" + } + } + }, + "tags": [] +} diff --git a/languages/rust/rust-client/src/lib.rs b/languages/rust/rust-client/src/lib.rs new file mode 100644 index 00000000..2caea450 --- /dev/null +++ b/languages/rust/rust-client/src/lib.rs @@ -0,0 +1,435 @@ +pub mod sse; +pub mod utils; +pub use async_trait::{self}; +pub use chrono::{self}; +pub use reqwest::{self, StatusCode}; +pub use serde_json::{self}; +use std::collections::HashMap; + +pub struct ArriClientConfig { + pub http_client: reqwest::Client, + pub base_url: String, + pub headers: fn() -> HashMap<&'static str, &'static str>, +} + +pub trait ArriClientService<'a> { + fn create(config: &'a ArriClientConfig) -> Self; +} + +pub struct ArriRequestOptions<'a> { + pub http_client: &'a reqwest::Client, + pub url: String, + pub method: reqwest::Method, + pub headers: fn() -> HashMap<&'static str, &'static str>, + pub client_version: String, +} + +pub struct ArriParsedRequestOptions<'a> { + pub http_client: &'a reqwest::Client, + pub url: String, + pub method: reqwest::Method, + pub headers: fn() -> HashMap<&'static str, &'static str>, + pub client_version: String, +} + +#[derive(Debug)] +pub struct ArriServerError { + pub code: u16, + pub message: String, + pub stack: Option, + pub data: Option, +} + +trait ArriRequestErrorMethods { + fn from_response_data(status: u16, body: String) -> Self; +} + +impl ArriRequestErrorMethods for ArriServerError { + fn from_response_data(status: u16, body: String) -> Self { + let mut err = Self::from_json_string(body.to_owned()); + if err.code == 0 { + err.code = status.to_owned(); + } + if err.message.is_empty() { + err.message = status_message_from_status_code(err.code); + } + err + } +} + +impl ArriModel for ArriServerError { + fn new() -> Self { + Self { + code: 0, + message: "".to_string(), + stack: None, + data: None, + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(val) => { + let code = match val.get("code") { + Some(serde_json::Value::Number(status_code_val)) => { + u16::try_from(status_code_val.to_owned().as_u64().unwrap_or_default()) + .unwrap_or(0) + } + _ => 0, + }; + let message = match val.get("message") { + Some(serde_json::Value::String(status_message_val)) => { + status_message_val.to_owned() + } + _ => "Unknown error".to_string(), + }; + let stack = match val.get("stack") { + Some(serde_json::Value::String(stack_val)) => Some(stack_val.to_owned()), + _ => None, + }; + let data = match val.get("data") { + Some(data_val) => Some(data_val.to_owned()), + _ => None, + }; + + Self { + code, + message, + stack, + data, + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut result = "{".to_string(); + result.push_str("\"code\":"); + result.push_str(format!("{}", &self.code).as_str()); + result.push_str(",\"message\":"); + result.push_str( + format!( + "\"{}\"", + &self.message.replace("\n", "\\n").replace("\"", "\\\"") + ) + .as_str(), + ); + match &self.stack { + Some(val) => { + result.push_str(",\"stack\":"); + result.push_str( + format!("\"{}\"", val.replace("\n", "\\n").replace("\"", "\\\"")).as_str(), + ); + } + _ => {} + } + match &self.data { + Some(val) => { + result.push_str(",\"data\":"); + result.push_str( + serde_json::to_string(val) + .unwrap_or("null".to_string()) + .as_str(), + ) + } + _ => {} + } + result + } + + fn to_query_params_string(&self) -> String { + let mut query_parts: Vec = Vec::new(); + query_parts.push(format!("code={}", &self.code)); + query_parts.push(format!("message={}", &self.message)); + match &self.stack { + Some(stack) => { + query_parts.push(format!("stack={}", stack)); + } + _ => {} + } + match &self.data { + Some(data) => { + query_parts.push(format!( + "data={}", + serde_json::to_string(data).unwrap_or("null".to_string()) + )); + } + _ => {} + } + query_parts.join("&") + } +} + +pub async fn arri_request<'a>( + opts: ArriRequestOptions<'a>, + params: Option, +) -> Result { + let response: Result; + let mut headers = (opts.headers)(); + match headers.get("Accept") { + Some(_) => {} + None => { + headers.insert("Accept", "application/json"); + } + } + if !opts.client_version.is_empty() { + headers.insert("client-version", opts.client_version.as_str()); + } + if opts.method != reqwest::Method::GET && opts.method != reqwest::Method::HEAD { + headers.insert("Content-Type", "application/json"); + } + let mut final_headers = reqwest::header::HeaderMap::new(); + for (key, value) in headers.into_iter() { + match reqwest::header::HeaderValue::from_str(value) { + Ok(header_val) => { + final_headers.insert(key, header_val); + } + Err(_) => { + println!( + "WARNING: Received invalid header value. key: \"{}\", value: \"{}\"", + key, value, + ); + } + } + } + match opts.method { + reqwest::Method::GET => { + let mut final_url = opts.url.clone(); + match params { + Some(val) => final_url = format!("{final_url}?{}", val.to_query_params_string()), + None => {} + } + response = opts + .http_client + .get(final_url) + .headers(final_headers) + .send() + .await; + } + reqwest::Method::POST => { + let builder = opts + .http_client + .post(opts.url.clone()) + .headers(final_headers); + match params { + Some(val) => { + response = builder.body(val.to_json_string()).send().await; + } + None => { + response = builder.send().await; + } + } + } + reqwest::Method::PUT => { + let builder = opts + .http_client + .put(opts.url.clone()) + .headers(final_headers); + match params { + Some(val) => { + response = builder.body(val.to_json_string()).send().await; + } + None => { + response = builder.send().await; + } + } + } + reqwest::Method::PATCH => { + let builder = opts + .http_client + .patch(opts.url.clone()) + .headers(final_headers); + match params { + Some(val) => { + response = builder.body(val.to_json_string()).send().await; + } + None => { + response = builder.send().await; + } + } + } + reqwest::Method::DELETE => { + let builder = opts + .http_client + .delete(opts.url.clone()) + .headers(final_headers); + match params { + Some(val) => { + response = builder.body(val.to_json_string()).send().await; + } + None => { + response = builder.send().await; + } + } + } + _ => panic!("Unsupported method"), + }; + match response { + Ok(res) => return Ok(res), + Err(err) => { + return Err(ArriServerError { + code: err.status().unwrap_or(StatusCode::default()).as_u16(), + message: format!("Error requesting \"{}\"", opts.url), + stack: None, + data: None, + }) + } + } +} + +pub trait ArriModel { + fn new() -> Self; + fn from_json(input: serde_json::Value) -> Self; + fn from_json_string(input: String) -> Self; + fn to_json_string(&self) -> String; + fn to_query_params_string(&self) -> String; +} + +pub trait ArriEnum { + fn default() -> Self; + fn from_string(input: String) -> Self; + fn serial_value(&self) -> String; +} + +pub struct EmptyArriModel {} +impl ArriModel for EmptyArriModel { + fn new() -> Self { + Self {} + } + + fn from_json(_: serde_json::Value) -> Self { + Self {} + } + + fn from_json_string(_: String) -> Self { + Self {} + } + + fn to_json_string(&self) -> String { + "{}".to_string() + } + + fn to_query_params_string(&self) -> String { + "".to_string() + } +} +pub trait ArriService { + fn new() -> Self; +} + +pub async fn parsed_arri_request<'a, TResponse>( + opts: ArriParsedRequestOptions<'a>, + params: Option, + parser: fn(body: String) -> TResponse, +) -> Result { + let result = arri_request( + ArriRequestOptions { + method: opts.method, + url: opts.url, + http_client: opts.http_client, + headers: opts.headers, + client_version: opts.client_version, + }, + params, + ) + .await; + if result.is_err() { + return Err(result.unwrap_err()); + } + let response = result.unwrap(); + let status = response.status().as_u16(); + let body: Result = response.text().await; + if status >= 300 || status < 200 { + return Err(ArriServerError::from_response_data( + status, + body.unwrap_or_default(), + )); + } + match body { + Ok(text) => return Ok(parser(text)), + Err(err) => { + return Err(ArriServerError { + code: status, + message: "Expected server to return plaintext".to_string(), + stack: None, + data: Some(serde_json::Value::String(err.to_string())), + }) + } + } +} + +fn status_message_from_status_code(status_code: u16) -> String { + match status_code { + 100 => String::from("Continue"), + 101 => String::from("Switching Protocols"), + 102 => String::from("Processing"), + 103 => String::from("Early Hints"), + 200 => String::from("OK"), + 201 => String::from("Created"), + 202 => String::from("Accepted"), + 203 => String::from("Non-Authoritative Information"), + 204 => String::from("No Content"), + 205 => String::from("Reset Content"), + 206 => String::from("Partial Content"), + 207 => String::from("Multi-Status"), + 208 => String::from("Already Reported"), + 226 => String::from("IM Used"), + 300 => String::from("Multiple Choices"), + 301 => String::from("Moved Permanently"), + 302 => String::from("Found"), + 303 => String::from("See Other"), + 304 => String::from("Not Modified"), + 305 => String::from("Use Proxy"), + 306 => String::from("unused"), + 307 => String::from("Temporary Redirect"), + 308 => String::from("Permanent Redirect"), + 400 => String::from("Bad Request"), + 401 => String::from("Unauthorized"), + 402 => String::from("Payment Required"), + 403 => String::from("Forbidden"), + 404 => String::from("Not Found"), + 405 => String::from("Method Not Allowed"), + 406 => String::from("Not Acceptable"), + 407 => String::from("Proxy Authentication Required"), + 408 => String::from("Request Timeout"), + 409 => String::from("Conflict"), + 410 => String::from("Gone"), + 411 => String::from("Length Required"), + 412 => String::from("Precondition Failed"), + 413 => String::from("Payload Too Large"), + 414 => String::from("URI Too Long"), + 415 => String::from("Unsupported Media Type"), + 416 => String::from("Range Not Satisfiable"), + 417 => String::from("Expectation Failed"), + 418 => String::from("I'm a teapot"), + 421 => String::from("Misdirected Request"), + 422 => String::from("Unprocessable Content"), + 423 => String::from("Locked"), + 424 => String::from("Failed Dependency"), + 425 => String::from("Too Early"), + 426 => String::from("Upgrade Required"), + 428 => String::from("Too Many Requests"), + 431 => String::from("Request Header Fields Too Large"), + 451 => String::from("Unavailable For Legal Reasons"), + 500 => String::from("Internal Server Error"), + 501 => String::from("Not Implemented"), + 502 => String::from("Bad Gateway"), + 503 => String::from("Service Unavailable"), + 504 => String::from("Gateway Timeout"), + 505 => String::from("HTTP Version Not Supported"), + 506 => String::from("Variant Also Negotiates"), + 507 => String::from("Insufficient Storage"), + 508 => String::from("Loop Detected"), + 510 => String::from("Not Extended"), + 511 => String::from("Network Authentication Required"), + _ => String::from("Unknown Error"), + } +} diff --git a/languages/rust/rust-client/src/sse.rs b/languages/rust/rust-client/src/sse.rs new file mode 100644 index 00000000..16e1fc97 --- /dev/null +++ b/languages/rust/rust-client/src/sse.rs @@ -0,0 +1,297 @@ +#![allow(dead_code)] +use reqwest::{header::HeaderMap, Response}; +use serde_json::json; +use std::marker::PhantomData; + +use crate::{ArriModel, ArriRequestErrorMethods, ArriServerError}; + +pub struct ParsedArriSseRequestOptions<'a, TMessage, OnMessage, OnError, OnOpen, OnClose> +where + OnMessage: Fn(TMessage), + OnError: Fn(ArriServerError), + OnOpen: Fn(&Response), + OnClose: Fn(&Response), +{ + pub client: &'a reqwest::Client, + pub url: String, + pub method: reqwest::Method, + pub headers: &'a HeaderMap, + pub on_message: Option, + pub on_error: Option, + pub on_open: Option, + pub on_close: Option, + _phantom_data_store: PhantomData, +} + +pub fn handle_message( + message: String, + parser: fn(String) -> TMessage, + on_message: fn(TMessage), +) { + on_message(parser(message)); +} + +pub async fn parsed_arri_sse_request< + 'a, + TMessage: ArriModel, + OnMessage, + OnError, + OnConnectionError, + OnOpen, + OnClose, +>( + options: ParsedArriSseRequestOptions<'a, TMessage, OnMessage, OnError, OnOpen, OnClose>, + params: Option, +) where + OnMessage: Fn(TMessage), + OnError: Fn(ArriServerError), + OnOpen: Fn(&Response), + OnClose: Fn(&Response), +{ + arri_sse_request( + ArriSseRequestOptions { + http_client: options.client, + url: options.url, + method: options.method, + headers: options.headers, + on_message: |input| match &options.on_message { + Some(func) => func(TMessage::from_json_string(input)), + None => {} + }, + on_error: |err| match &options.on_error { + Some(func) => func(err), + None => {} + }, + on_close: |res| match &options.on_close { + Some(func) => func(res), + None => {} + }, + on_open: |res| match &options.on_open { + Some(func) => func(res), + None => {} + }, + }, + params, + ) + .await +} + +pub struct ArriSseRequestOptions<'a, OnMessage, OnError, OnOpen, OnClose> +where + OnMessage: Fn(String), + OnError: Fn(ArriServerError), + OnOpen: Fn(&Response), + OnClose: Fn(&Response), +{ + http_client: &'a reqwest::Client, + url: String, + method: reqwest::Method, + headers: &'a HeaderMap, + on_message: OnMessage, + on_error: OnError, + on_open: OnOpen, + on_close: OnClose, +} + +pub async fn arri_sse_request<'a, OnMessage, OnError, OnOpen, OnClose>( + options: ArriSseRequestOptions<'a, OnMessage, OnError, OnOpen, OnClose>, + params: Option, +) -> () +where + OnMessage: Fn(String), + OnError: Fn(ArriServerError), + OnOpen: Fn(&Response), + OnClose: Fn(&Response), +{ + let query_string: Option; + let json_body: Option; + + match params { + Some(val) => match options.method { + reqwest::Method::GET => { + query_string = Some(val.to_query_params_string()); + json_body = None; + } + _ => { + query_string = None; + json_body = Some(val.to_json_string()); + } + }, + None => { + query_string = None; + json_body = None; + } + } + + let url = match query_string { + Some(val) => format!("{}?{}", options.url, val), + None => options.url, + }; + + let response = match json_body { + Some(body) => { + options + .http_client + .request(options.method, url) + .headers(options.headers.to_owned()) + .body(body) + .send() + .await + } + None => { + options + .http_client + .request(options.method, url) + .headers(options.headers.to_owned()) + .send() + .await + } + }; + + if !response.is_ok() { + (options.on_error)(ArriServerError::new()); + return; + } + let mut ok_response = response.unwrap(); + (options.on_open)(&ok_response); + let status = ok_response.status().as_u16(); + if status < 200 || status >= 300 { + let body = ok_response.text().await.unwrap_or_default(); + (options.on_error)(ArriServerError::from_response_data(status, body)); + return; + } + let mut pending_data: String = "".to_string(); + while let Some(chunk) = ok_response.chunk().await.unwrap_or_default() { + let chunk_vec = chunk.to_vec(); + let data = std::str::from_utf8(chunk_vec.as_slice()); + match data { + Ok(text) => { + if !text.ends_with("\n\n") { + pending_data.push_str(text); + continue; + } + let msg_text = format!("{}{}", pending_data, text); + pending_data = String::from(""); + let messages = sse_messages_from_string(msg_text); + for message in messages { + let event = message.event.unwrap_or("".to_string()); + match event.as_str() { + "done" => { + (options.on_close)(&ok_response); + break; + } + _ => { + (options.on_message)(message.data); + } + } + } + } + _ => {} + } + println!("Chunk: {:?}", chunk); + } +} + +#[derive(Debug, Clone)] +pub struct SseMessage { + id: Option, + event: Option, + data: String, + retry: Option, +} + +pub trait SeeMessageMethods { + fn new() -> Self; + fn from_string(input: String) -> Self; +} + +impl SeeMessageMethods for SseMessage { + fn new() -> Self { + Self { + id: None, + event: None, + data: String::from(""), + retry: None, + } + } + fn from_string(input: String) -> Self { + let parts = input.split("\n"); + let mut id: Option = None; + let mut event: Option = None; + let mut data = String::from(""); + let mut retry: Option = None; + for part in parts { + let trimmed = part.trim(); + if trimmed.starts_with("id:") { + let sub_str = &trimmed[3..trimmed.len()]; + id = Some(sub_str.trim().to_string()); + continue; + } + if trimmed.starts_with("event:") { + let sub_str = &trimmed[5..trimmed.len()]; + event = Some(sub_str.trim().to_string()); + continue; + } + if trimmed.starts_with("data:") { + let sub_str = &trimmed[4..trimmed.len()]; + data = sub_str.trim().to_string(); + continue; + } + if trimmed.starts_with("retry:") { + let sub_str = &trimmed[5..trimmed.len()]; + let result = json!(sub_str.trim()); + match result { + serde_json::Value::Number(val) => { + retry = Some( + i32::try_from(val.as_i64().unwrap_or_default()).unwrap_or_default(), + ); + } + _ => retry = None, + } + } + } + Self { + id, + event, + data, + retry, + } + } +} + +struct ParsedSseMessage { + pub id: Option, + pub event: Option, + pub data: T, + pub retry: Option, +} + +impl SeeMessageMethods for ParsedSseMessage { + fn new() -> Self { + Self { + id: None, + event: None, + data: T::new(), + retry: None, + } + } + fn from_string(input: String) -> Self { + let message = SseMessage::from_string(input); + Self { + id: message.id, + event: message.event, + data: T::from_json_string(message.data), + retry: message.retry, + } + } +} + +fn sse_messages_from_string(input: String) -> Vec { + let parts = input.split("\n\n"); + let mut messages: Vec = Vec::new(); + for part in parts { + let msg = SseMessage::from_string(part.to_string()); + messages.push(msg); + } + messages +} diff --git a/languages/rust/rust-client/src/utils.rs b/languages/rust/rust-client/src/utils.rs new file mode 100644 index 00000000..8d2d9c2d --- /dev/null +++ b/languages/rust/rust-client/src/utils.rs @@ -0,0 +1,13 @@ +use chrono::{DateTime, FixedOffset, SecondsFormat}; + +pub fn serialize_string(input: &String) -> String { + serde_json::to_string(input).unwrap_or("".to_string()) +} + +pub fn serialize_date_time(input: &DateTime, wrap_in_quotes: bool) -> String { + let result = input.to_rfc3339_opts(SecondsFormat::Millis, true); + match wrap_in_quotes { + true => format!("\"{}\"", result), + false => result, + } +} diff --git a/languages/rust/rust-codegen-reference/.clippy.toml b/languages/rust/rust-codegen-reference/.clippy.toml new file mode 100644 index 00000000..7be36935 --- /dev/null +++ b/languages/rust/rust-codegen-reference/.clippy.toml @@ -0,0 +1 @@ +# config for rust lints \ No newline at end of file diff --git a/languages/rust/rust-codegen-reference/Cargo.lock b/languages/rust/rust-codegen-reference/Cargo.lock new file mode 100644 index 00000000..85cc5acb --- /dev/null +++ b/languages/rust/rust-codegen-reference/Cargo.lock @@ -0,0 +1,1364 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arri_client" +version = "0.51.2" +dependencies = [ + "async-trait", + "chrono", + "regex", + "reqwest", + "serde_json", +] + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.5", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rust_reference" +version = "0.1.0" +dependencies = [ + "arri_client", + "tokio", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "syn" +version = "2.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/languages/rust/rust-codegen-reference/Cargo.toml b/languages/rust/rust-codegen-reference/Cargo.toml new file mode 100644 index 00000000..becbb5b8 --- /dev/null +++ b/languages/rust/rust-codegen-reference/Cargo.toml @@ -0,0 +1,12 @@ + +[package] +name = "rust_reference" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +arri_client = { path = "../rust-client" } +tokio = {version = "1.36.0", features = ["full"]} diff --git a/languages/rust/rust-codegen-reference/project.json b/languages/rust/rust-codegen-reference/project.json new file mode 100644 index 00000000..36ae6c43 --- /dev/null +++ b/languages/rust/rust-codegen-reference/project.json @@ -0,0 +1,27 @@ +{ + "name": "rust-codegen-reference", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "implicitDependencies": ["rust-client"], + "targets": { + "cargo": { + "executor": "nx:run-commands", + "options": { + "command": "cargo", + "cwd": "languages/rust/rust-codegen-reference" + } + }, + "test": { + "executor": "nx:run-commands", + "inputs": [ + "{projectRoot}/src/**/*.rs", + "{projectRoot}/Cargo.toml", + "{projectRoot}/Cargo.lock" + ], + "outputs": ["{projectRoot}/target"], + "options": { + "command": "cargo test", + "cwd": "languages/rust/rust-codegen-reference" + } + } + } +} diff --git a/languages/rust/rust-codegen-reference/src/example_client.rs b/languages/rust/rust-codegen-reference/src/example_client.rs new file mode 100644 index 00000000..b5dd3744 --- /dev/null +++ b/languages/rust/rust-codegen-reference/src/example_client.rs @@ -0,0 +1,1981 @@ +#![allow( + dead_code, + unused_imports, + unused_variables, + unconditional_recursion, + deprecated +)] +use arri_client::{ + chrono::{DateTime, FixedOffset}, + parsed_arri_request, reqwest, serde_json, + utils::{serialize_date_time, serialize_string}, + ArriClientConfig, ArriClientService, ArriEnum, ArriModel, ArriParsedRequestOptions, + ArriServerError, EmptyArriModel, +}; +use std::collections::BTreeMap; + +pub struct ExampleClient<'a> { + config: &'a ArriClientConfig, + pub books: ExampleClientBooksService<'a>, +} + +impl<'a> ArriClientService<'a> for ExampleClient<'a> { + fn create(config: &'a ArriClientConfig) -> Self { + Self { + config: &config, + books: ExampleClientBooksService::create(config), + } + } +} + +impl ExampleClient<'_> { + pub async fn send_object( + self: &Self, + params: NestedObject, + ) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/send-object", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "20".to_string(), + }, + Some(params), + |body| return NestedObject::from_json_string(body), + ) + .await + } +} + +pub struct ExampleClientBooksService<'a> { + config: &'a ArriClientConfig, +} + +impl<'a> ArriClientService<'a> for ExampleClientBooksService<'a> { + fn create(config: &'a ArriClientConfig) -> Self { + Self { config: &config } + } +} + +impl ExampleClientBooksService<'_> { + pub async fn get_book(self: &Self, params: BookParams) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/books/get-book", &self.config.base_url), + method: reqwest::Method::GET, + headers: self.config.headers, + client_version: "20".to_string(), + }, + Some(params), + |body| return Book::from_json_string(body), + ) + .await + } + pub async fn create_book(self: &Self, params: Book) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/books/create-book", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "20".to_string(), + }, + Some(params), + |body| return Book::from_json_string(body), + ) + .await + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Book { + pub id: String, + pub name: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +impl ArriModel for Book { + fn new() -> Self { + Self { + id: "".to_string(), + name: "".to_string(), + created_at: DateTime::default(), + updated_at: DateTime::default(), + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let name = match _val_.get("name") { + Some(serde_json::Value::String(name_val)) => name_val.to_owned(), + _ => "".to_string(), + }; + let created_at = match _val_.get("createdAt") { + Some(serde_json::Value::String(created_at_val)) => { + DateTime::::parse_from_rfc3339(created_at_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let updated_at = match _val_.get("updatedAt") { + Some(serde_json::Value::String(updated_at_val)) => { + DateTime::::parse_from_rfc3339(updated_at_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + Self { + id, + name, + created_at, + updated_at, + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"name\":"); + _json_output_.push_str(serialize_string(&self.name).as_str()); + _json_output_.push_str(",\"createdAt\":"); + _json_output_.push_str(serialize_date_time(&self.created_at, true).as_str()); + _json_output_.push_str(",\"updatedAt\":"); + _json_output_.push_str(serialize_date_time(&self.updated_at, true).as_str()); + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!("name={}", &self.name)); + _query_parts_.push(format!( + "createdAt={}", + serialize_date_time(&self.created_at, false) + )); + _query_parts_.push(format!( + "updatedAt={}", + serialize_date_time(&self.updated_at, false) + )); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct BookParams { + pub book_id: String, +} + +impl ArriModel for BookParams { + fn new() -> Self { + Self { + book_id: "".to_string(), + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let book_id = match _val_.get("bookId") { + Some(serde_json::Value::String(book_id_val)) => book_id_val.to_owned(), + _ => "".to_string(), + }; + Self { book_id } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + _json_output_.push_str("\"bookId\":"); + _json_output_.push_str(serialize_string(&self.book_id).as_str()); + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("bookId={}", &self.book_id)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct NestedObject { + pub id: String, + pub content: String, +} + +impl ArriModel for NestedObject { + fn new() -> Self { + Self { + id: "".to_string(), + content: "".to_string(), + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let content = match _val_.get("content") { + Some(serde_json::Value::String(content_val)) => content_val.to_owned(), + _ => "".to_string(), + }; + Self { id, content } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"content\":"); + _json_output_.push_str(serialize_string(&self.content).as_str()); + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!("content={}", &self.content)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryType { + pub string: String, + pub boolean: bool, + pub timestamp: DateTime, + pub float32: f32, + pub float64: f64, + pub int8: i8, + pub uint8: u8, + pub int16: i16, + pub uint16: u16, + pub int32: i32, + pub uint32: u32, + pub int64: i64, + pub uint64: u64, + pub r#enum: Enumerator, + pub object: NestedObject, + pub array: Vec, + pub record: BTreeMap, + pub discriminator: Discriminator, + pub any: serde_json::Value, +} + +impl ArriModel for ObjectWithEveryType { + fn new() -> Self { + Self { + string: "".to_string(), + boolean: false, + timestamp: DateTime::default(), + float32: 0.0, + float64: 0.0, + int8: 0, + uint8: 0, + int16: 0, + uint16: 0, + int32: 0, + uint32: 0, + int64: 0, + uint64: 0, + r#enum: Enumerator::default(), + object: NestedObject::new(), + array: Vec::new(), + record: BTreeMap::new(), + discriminator: Discriminator::new(), + any: serde_json::Value::Null, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => string_val.to_owned(), + _ => "".to_string(), + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => boolean_val.to_owned(), + _ => false, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let float32 = match _val_.get("float32") { + Some(serde_json::Value::Number(float32_val)) => { + float32_val.as_f64().unwrap_or(0.0) as f32 + } + _ => 0.0, + }; + let float64 = match _val_.get("float64") { + Some(serde_json::Value::Number(float64_val)) => { + float64_val.as_f64().unwrap_or(0.0) + } + _ => 0.0, + }; + let int8 = match _val_.get("int8") { + Some(serde_json::Value::Number(int8_val)) => { + i8::try_from(int8_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let uint8 = match _val_.get("uint8") { + Some(serde_json::Value::Number(uint8_val)) => { + u8::try_from(uint8_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let int16 = match _val_.get("int16") { + Some(serde_json::Value::Number(int16_val)) => { + i16::try_from(int16_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let uint16 = match _val_.get("uint16") { + Some(serde_json::Value::Number(uint16_val)) => { + u16::try_from(uint16_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let int32 = match _val_.get("int32") { + Some(serde_json::Value::Number(int32_val)) => { + i32::try_from(int32_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let uint32 = match _val_.get("uint32") { + Some(serde_json::Value::Number(uint32_val)) => { + u32::try_from(uint32_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let int64 = match _val_.get("int64") { + Some(serde_json::Value::String(int64_val)) => { + int64_val.parse::().unwrap_or(0) + } + _ => 0, + }; + let uint64 = match _val_.get("uint64") { + Some(serde_json::Value::String(uint64_val)) => { + uint64_val.parse::().unwrap_or(0) + } + _ => 0, + }; + let r#enum = match _val_.get("enum") { + Some(serde_json::Value::String(enum_val)) => { + Enumerator::from_string(enum_val.to_owned()) + } + _ => Enumerator::default(), + }; + let object = match _val_.get("object") { + Some(object_val) => NestedObject::from_json(object_val.to_owned()), + _ => NestedObject::new(), + }; + let array = match _val_.get("array") { + Some(serde_json::Value::Array(array_val)) => { + let mut array_val_result: Vec = Vec::new(); + for array_val_element in array_val { + array_val_result.push(match Some(array_val_element) { + Some(serde_json::Value::Bool(array_val_element_val)) => { + array_val_element_val.to_owned() + } + _ => false, + }); + } + array_val_result + } + _ => Vec::new(), + }; + let record = match _val_.get("record") { + Some(serde_json::Value::Object(record_val)) => { + let mut record_val_result: BTreeMap = BTreeMap::new(); + for (_key_, _value_) in record_val.into_iter() { + record_val_result.insert( + _key_.to_owned(), + match Some(_value_.to_owned()) { + Some(serde_json::Value::Bool(value_val)) => { + value_val.to_owned() + } + _ => false, + }, + ); + } + record_val_result + } + _ => BTreeMap::new(), + }; + let discriminator = match _val_.get("discriminator") { + Some(discriminator_val) => match discriminator_val { + serde_json::Value::Object(_) => { + Discriminator::from_json(discriminator_val.to_owned()) + } + _ => Discriminator::new(), + }, + _ => Discriminator::new(), + }; + let any = match _val_.get("any") { + Some(any_val) => any_val.to_owned(), + _ => serde_json::Value::Null, + }; + + Self { + string, + boolean, + timestamp, + float32, + float64, + int8, + uint8, + int16, + uint16, + int32, + uint32, + int64, + uint64, + r#enum, + object, + array, + record, + discriminator, + any, + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + _json_output_.push_str("\"string\":"); + _json_output_.push_str(serialize_string(&self.string).as_str()); + _json_output_.push_str(",\"boolean\":"); + _json_output_.push_str(&self.boolean.to_string().as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push_str(",\"float32\":"); + _json_output_.push_str(&self.float32.to_string().as_str()); + _json_output_.push_str(",\"float64\":"); + _json_output_.push_str(&self.float64.to_string().as_str()); + _json_output_.push_str(",\"int8\":"); + _json_output_.push_str(&self.int8.to_string().as_str()); + _json_output_.push_str(",\"uint8\":"); + _json_output_.push_str(&self.uint8.to_string().as_str()); + _json_output_.push_str(",\"int16\":"); + _json_output_.push_str(&self.int16.to_string().as_str()); + _json_output_.push_str(",\"uint16\":"); + _json_output_.push_str(&self.uint16.to_string().as_str()); + _json_output_.push_str(",\"int32\":"); + _json_output_.push_str(&self.int32.to_string().as_str()); + _json_output_.push_str(",\"uint32\":"); + _json_output_.push_str(&self.uint32.to_string().as_str()); + _json_output_.push_str(",\"int64\":"); + _json_output_.push_str(format!("\"{}\"", &self.int64).as_str()); + _json_output_.push_str(",\"uint64\":"); + _json_output_.push_str(format!("\"{}\"", &self.uint64).as_str()); + _json_output_.push_str(",\"enum\":"); + _json_output_.push_str(format!("\"{}\"", &self.r#enum.serial_value()).as_str()); + _json_output_.push_str(",\"object\":"); + _json_output_.push_str(&self.object.to_json_string().as_str()); + _json_output_.push_str(",\"array\":"); + _json_output_.push('['); + for (_index_, _element_) in self.array.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_string().as_str()); + } + _json_output_.push(']'); + _json_output_.push_str(",\"record\":"); + _json_output_.push('{'); + for (_index_, (_key_, _value_)) in self.record.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(format!("\"{}\":", _key_).as_str()); + _json_output_.push_str(_value_.to_string().as_str()); + } + _json_output_.push('}'); + _json_output_.push_str(",\"discriminator\":"); + _json_output_.push_str(&self.discriminator.to_json_string().as_str()); + _json_output_.push_str(",\"any\":"); + _json_output_.push_str( + serde_json::to_string(&self.any) + .unwrap_or("null".to_string()) + .as_str(), + ); + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("string={}", &self.string)); + _query_parts_.push(format!("boolean={}", &self.boolean)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + _query_parts_.push(format!("float32={}", &self.float32)); + _query_parts_.push(format!("float64={}", &self.float64)); + _query_parts_.push(format!("int8={}", &self.int8)); + _query_parts_.push(format!("uint8={}", &self.uint8)); + _query_parts_.push(format!("int16={}", &self.int16)); + _query_parts_.push(format!("uint16={}", &self.uint16)); + _query_parts_.push(format!("int32={}", &self.int32)); + _query_parts_.push(format!("uint32={}", &self.uint32)); + _query_parts_.push(format!("int64={}", &self.int64)); + _query_parts_.push(format!("uint64={}", &self.uint64)); + _query_parts_.push(format!("enum={}", &self.r#enum.serial_value())); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryType/object."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithEveryType/array."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryType/record."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryType/discriminator."); + println!("[WARNING] cannot serialize any's to query params. Skipping field at /ObjectWithEveryType/any."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Enumerator { + Foo, + Bar, + Baz, +} + +impl ArriEnum for Enumerator { + fn default() -> Self { + Enumerator::Foo + } + + fn from_string(input: String) -> Self { + match input.as_str() { + "FOO" => Self::Foo, + "BAR" => Self::Bar, + "BAZ" => Self::Baz, + _ => Self::default(), + } + } + + fn serial_value(&self) -> String { + match &self { + Enumerator::Foo => "FOO".to_string(), + Enumerator::Bar => "BAR".to_string(), + Enumerator::Baz => "BAZ".to_string(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Discriminator { + A { + id: String, + }, + B { + id: String, + name: String, + }, + C { + id: String, + name: String, + date: DateTime, + }, +} + +impl ArriModel for Discriminator { + fn new() -> Self { + Self::A { id: "".to_string() } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let type_name = match _val_.get("typeName") { + Some(serde_json::Value::String(type_name_val)) => type_name_val.to_owned(), + _ => "".to_string(), + }; + match type_name.as_str() { + "A" => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + Self::A { id } + } + "B" => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let name = match _val_.get("name") { + Some(serde_json::Value::String(name_val)) => name_val.to_owned(), + _ => "".to_string(), + }; + Self::B { id, name } + } + "C" => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let name = match _val_.get("name") { + Some(serde_json::Value::String(name_val)) => name_val.to_owned(), + _ => "".to_string(), + }; + let date = match _val_.get("date") { + Some(serde_json::Value::String(date_val)) => { + DateTime::::parse_from_rfc3339(date_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + Self::C { id, name, date } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::A { id } => { + _json_output_.push_str("\"typeName\":\"A\""); + _json_output_.push_str(",\"id\":"); + _json_output_.push_str(serialize_string(id).as_str()); + } + Self::B { id, name } => { + _json_output_.push_str("\"typeName\":\"B\""); + _json_output_.push_str(",\"id\":"); + _json_output_.push_str(serialize_string(id).as_str()); + _json_output_.push_str(",\"name\":"); + _json_output_.push_str(serialize_string(name).as_str()); + } + Self::C { id, name, date } => { + _json_output_.push_str("\"typeName\":\"C\""); + _json_output_.push_str(",\"id\":"); + _json_output_.push_str(serialize_string(id).as_str()); + _json_output_.push_str(",\"name\":"); + _json_output_.push_str(serialize_string(name).as_str()); + _json_output_.push_str(",\"date\":"); + _json_output_.push_str(serialize_date_time(date, true).as_str()); + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::A { id } => { + _query_parts_.push(format!("typeName=A")); + _query_parts_.push(format!("id={}", id)); + } + Self::B { id, name } => { + _query_parts_.push(format!("typeName=B")); + _query_parts_.push(format!("id={}", id)); + _query_parts_.push(format!("name={}", name)); + } + Self::C { id, name, date } => { + _query_parts_.push(format!("typeName=C")); + _query_parts_.push(format!("id={}", id)); + _query_parts_.push(format!("name={}", name)); + _query_parts_.push(format!("date={}", serialize_date_time(date, false))); + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithOptionalFields { + pub string: Option, + pub boolean: Option, + pub timestamp: Option>, + pub float32: Option, + pub float64: Option, + pub int8: Option, + pub uint8: Option, + pub int16: Option, + pub uint16: Option, + pub int32: Option, + pub uint32: Option, + pub int64: Option, + pub uint64: Option, + pub r#enum: Option, + pub object: Option, + pub array: Option>, + pub record: Option>, + pub discriminator: Option, + pub any: Option, +} + +impl ArriModel for ObjectWithOptionalFields { + fn new() -> Self { + Self { + string: None, + boolean: None, + timestamp: None, + float32: None, + float64: None, + int8: None, + uint8: None, + int16: None, + uint16: None, + int32: None, + uint32: None, + int64: None, + uint64: None, + r#enum: None, + object: None, + array: None, + record: None, + discriminator: None, + any: None, + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => Some(string_val.to_owned()), + _ => None, + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => Some(boolean_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + let float32 = match _val_.get("float32") { + Some(serde_json::Value::Number(float32_val)) => match float32_val.as_f64() { + Some(float32_val_result) => Some(float32_val_result as f32), + _ => None, + }, + _ => None, + }; + let float64 = match _val_.get("float64") { + Some(serde_json::Value::Number(float64_val)) => match float64_val.as_f64() { + Some(float64_val_result) => Some(float64_val_result), + _ => None, + }, + _ => None, + }; + let int8 = match _val_.get("int8") { + Some(serde_json::Value::Number(int8_val)) => match int8_val.as_i64() { + Some(int8_val_result) => match i8::try_from(int8_val_result) { + Ok(int8_val_result_val) => Some(int8_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint8 = match _val_.get("uint8") { + Some(serde_json::Value::Number(uint8_val)) => match uint8_val.as_u64() { + Some(uint8_val_result) => match u8::try_from(uint8_val_result) { + Ok(uint8_val_result_val) => Some(uint8_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int16 = match _val_.get("int16") { + Some(serde_json::Value::Number(int16_val)) => match int16_val.as_i64() { + Some(int16_val_result) => match i16::try_from(int16_val_result) { + Ok(int16_val_result_val) => Some(int16_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint16 = match _val_.get("uint16") { + Some(serde_json::Value::Number(uint16_val)) => match uint16_val.as_u64() { + Some(uint16_val_result) => match u16::try_from(uint16_val_result) { + Ok(uint16_val_result_val) => Some(uint16_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int32 = match _val_.get("int32") { + Some(serde_json::Value::Number(int32_val)) => match int32_val.as_i64() { + Some(int32_val_result) => match i32::try_from(int32_val_result) { + Ok(int32_val_result_val) => Some(int32_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint32 = match _val_.get("uint32") { + Some(serde_json::Value::Number(uint32_val)) => match uint32_val.as_u64() { + Some(uint32_val_result) => match u32::try_from(uint32_val_result) { + Ok(uint32_val_result_val) => Some(uint32_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int64 = match _val_.get("int64") { + Some(serde_json::Value::String(int64_val)) => match int64_val.parse::() { + Ok(int64_val_result) => Some(int64_val_result), + Err(_) => None, + }, + _ => None, + }; + let uint64 = match _val_.get("uint64") { + Some(serde_json::Value::String(uint64_val)) => { + match uint64_val.parse::() { + Ok(uint64_val_result) => Some(uint64_val_result), + Err(_) => None, + } + } + _ => None, + }; + let r#enum = match _val_.get("enum") { + Some(serde_json::Value::String(enum_val)) => { + Some(Enumerator::from_string(enum_val.to_owned())) + } + _ => None, + }; + let object = match _val_.get("object") { + Some(object_val) => match object_val { + serde_json::Value::Object(_) => { + Some(NestedObject::from_json(object_val.to_owned())) + } + _ => None, + }, + _ => None, + }; + let array = match _val_.get("array") { + Some(serde_json::Value::Array(array_val)) => { + let mut array_val_result: Vec = Vec::new(); + for array_val_element in array_val { + array_val_result.push(match Some(array_val_element) { + Some(serde_json::Value::Bool(array_val_element_val)) => { + array_val_element_val.to_owned() + } + _ => false, + }); + } + Some(array_val_result) + } + _ => None, + }; + let record = match _val_.get("record") { + Some(serde_json::Value::Object(record_val)) => { + let mut record_val_result: BTreeMap = BTreeMap::new(); + for (_key_, _value_) in record_val.into_iter() { + record_val_result.insert( + _key_.to_owned(), + match Some(_value_.to_owned()) { + Some(serde_json::Value::Bool(value_val)) => { + value_val.to_owned() + } + _ => false, + }, + ); + } + Some(record_val_result) + } + _ => None, + }; + let discriminator = match _val_.get("discriminator") { + Some(discriminator_val) => match discriminator_val { + serde_json::Value::Object(_) => { + Some(Discriminator::from_json(discriminator_val.to_owned())) + } + _ => None, + }, + _ => None, + }; + let any = match _val_.get("any") { + Some(any_val) => Some(any_val.to_owned()), + _ => None, + }; + + Self { + string, + boolean, + timestamp, + float32, + float64, + int8, + uint8, + int16, + uint16, + int32, + uint32, + int64, + uint64, + r#enum, + object, + array, + record, + discriminator, + any, + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + let mut _has_keys_ = false; + match &self.string { + Some(string_val) => { + _json_output_.push_str("\"string\":"); + _json_output_.push_str(serialize_string(string_val).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.boolean { + Some(boolean_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"boolean\":"); + _json_output_.push_str(boolean_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.timestamp { + Some(timestamp_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"timestamp\":"); + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.float32 { + Some(float32_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"float32\":"); + _json_output_.push_str(float32_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.float64 { + Some(float64_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"float64\":"); + _json_output_.push_str(float64_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.int8 { + Some(int8_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"int8\":"); + _json_output_.push_str(int8_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.uint8 { + Some(uint8_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"uint8\":"); + _json_output_.push_str(uint8_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.int16 { + Some(int16_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"int16\":"); + _json_output_.push_str(int16_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.uint16 { + Some(uint16_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"uint16\":"); + _json_output_.push_str(uint16_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.int32 { + Some(int32_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"int32\":"); + _json_output_.push_str(int32_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.uint32 { + Some(uint32_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"uint32\":"); + _json_output_.push_str(uint32_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.int64 { + Some(int64_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"int64\":"); + _json_output_.push_str(format!("\"{}\"", int64_val).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.uint64 { + Some(uint64_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"uint64\":"); + _json_output_.push_str(format!("\"{}\"", uint64_val).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.r#enum { + Some(enum_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"enum\":"); + _json_output_.push_str(format!("\"{}\"", enum_val.serial_value()).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.object { + Some(object_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"object\":"); + _json_output_.push_str(object_val.to_json_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.array { + Some(array_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"array\":"); + _json_output_.push('['); + for (_index_, _element_) in array_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_string().as_str()); + } + _json_output_.push(']'); + _has_keys_ = true; + } + _ => {} + }; + match &self.record { + Some(record_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"record\":"); + _json_output_.push('{'); + for (_index_, (_key_, _value_)) in record_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(format!("\"{}\":", _key_).as_str()); + _json_output_.push_str(_value_.to_string().as_str()); + } + _json_output_.push('}'); + _has_keys_ = true; + } + _ => {} + }; + match &self.discriminator { + Some(discriminator_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"discriminator\":"); + _json_output_.push_str(discriminator_val.to_json_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.any { + Some(any_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"any\":"); + _json_output_.push_str( + serde_json::to_string(any_val) + .unwrap_or("null".to_string()) + .as_str(), + ); + } + _ => {} + }; + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self.string { + Some(string_val) => { + _query_parts_.push(format!("string={}", string_val)); + } + _ => {} + }; + match &self.boolean { + Some(boolean_val) => { + _query_parts_.push(format!("boolean={}", boolean_val)); + } + _ => {} + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => {} + }; + match &self.float32 { + Some(float32_val) => { + _query_parts_.push(format!("float32={}", float32_val)); + } + _ => {} + }; + match &self.float64 { + Some(float64_val) => { + _query_parts_.push(format!("float64={}", float64_val)); + } + _ => {} + }; + match &self.int8 { + Some(int8_val) => { + _query_parts_.push(format!("int8={}", int8_val)); + } + _ => {} + }; + match &self.uint8 { + Some(uint8_val) => { + _query_parts_.push(format!("uint8={}", uint8_val)); + } + _ => {} + }; + match &self.int16 { + Some(int16_val) => { + _query_parts_.push(format!("int16={}", int16_val)); + } + _ => {} + }; + match &self.uint16 { + Some(uint16_val) => { + _query_parts_.push(format!("uint16={}", uint16_val)); + } + _ => {} + }; + match &self.int32 { + Some(int32_val) => { + _query_parts_.push(format!("int32={}", int32_val)); + } + _ => {} + }; + match &self.uint32 { + Some(uint32_val) => { + _query_parts_.push(format!("uint32={}", uint32_val)); + } + _ => {} + }; + match &self.int64 { + Some(int64_val) => { + _query_parts_.push(format!("int64={}", int64_val)); + } + _ => {} + }; + match &self.uint64 { + Some(uint64_val) => { + _query_parts_.push(format!("uint64={}", uint64_val)); + } + _ => {} + }; + match &self.r#enum { + Some(enum_val) => { + _query_parts_.push(format!("enum={}", enum_val.serial_value())); + } + _ => {} + }; + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithOptionalFields/object."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithOptionalFields/array."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithOptionalFields/record."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithOptionalFields/discriminator."); + println!("[WARNING] cannot serialize any's to query params. Skipping field at /ObjectWithOptionalFields/any."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithNullableFields { + pub string: Option, + pub boolean: Option, + pub timestamp: Option>, + pub float32: Option, + pub float64: Option, + pub int8: Option, + pub uint8: Option, + pub int16: Option, + pub uint16: Option, + pub int32: Option, + pub uint32: Option, + pub int64: Option, + pub uint64: Option, + pub r#enum: Option, + pub object: Option, + pub array: Option>, + pub record: Option>, + pub discriminator: Option, + pub any: serde_json::Value, +} + +impl ArriModel for ObjectWithNullableFields { + fn new() -> Self { + Self { + string: None, + boolean: None, + timestamp: None, + float32: None, + float64: None, + int8: None, + uint8: None, + int16: None, + uint16: None, + int32: None, + uint32: None, + int64: None, + uint64: None, + r#enum: None, + object: None, + array: None, + record: None, + discriminator: None, + any: serde_json::Value::Null, + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => Some(string_val.to_owned()), + _ => None, + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => Some(boolean_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + let float32 = match _val_.get("float32") { + Some(serde_json::Value::Number(float32_val)) => match float32_val.as_f64() { + Some(float32_val_result) => Some(float32_val_result as f32), + _ => None, + }, + _ => None, + }; + let float64 = match _val_.get("float64") { + Some(serde_json::Value::Number(float64_val)) => match float64_val.as_f64() { + Some(float64_val_result) => Some(float64_val_result), + _ => None, + }, + _ => None, + }; + let int8 = match _val_.get("int8") { + Some(serde_json::Value::Number(int8_val)) => match int8_val.as_i64() { + Some(int8_val_result) => match i8::try_from(int8_val_result) { + Ok(int8_val_result_val) => Some(int8_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint8 = match _val_.get("uint8") { + Some(serde_json::Value::Number(uint8_val)) => match uint8_val.as_u64() { + Some(uint8_val_result) => match u8::try_from(uint8_val_result) { + Ok(uint8_val_result_val) => Some(uint8_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int16 = match _val_.get("int16") { + Some(serde_json::Value::Number(int16_val)) => match int16_val.as_i64() { + Some(int16_val_result) => match i16::try_from(int16_val_result) { + Ok(int16_val_result_val) => Some(int16_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint16 = match _val_.get("uint16") { + Some(serde_json::Value::Number(uint16_val)) => match uint16_val.as_u64() { + Some(uint16_val_result) => match u16::try_from(uint16_val_result) { + Ok(uint16_val_result_val) => Some(uint16_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int32 = match _val_.get("int32") { + Some(serde_json::Value::Number(int32_val)) => match int32_val.as_i64() { + Some(int32_val_result) => match i32::try_from(int32_val_result) { + Ok(int32_val_result_val) => Some(int32_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint32 = match _val_.get("uint32") { + Some(serde_json::Value::Number(uint32_val)) => match uint32_val.as_u64() { + Some(uint32_val_result) => match u32::try_from(uint32_val_result) { + Ok(uint32_val_result_val) => Some(uint32_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int64 = match _val_.get("int64") { + Some(serde_json::Value::String(int64_val)) => match int64_val.parse::() { + Ok(int64_val_result) => Some(int64_val_result), + Err(_) => None, + }, + _ => None, + }; + let uint64 = match _val_.get("uint64") { + Some(serde_json::Value::String(uint64_val)) => { + match uint64_val.parse::() { + Ok(uint64_val_result) => Some(uint64_val_result), + Err(_) => None, + } + } + _ => None, + }; + let r#enum = match _val_.get("enum") { + Some(serde_json::Value::String(enum_val)) => { + Some(Enumerator::from_string(enum_val.to_owned())) + } + _ => None, + }; + let object = match _val_.get("object") { + Some(object_val) => match object_val { + serde_json::Value::Object(_) => { + Some(NestedObject::from_json(object_val.to_owned())) + } + _ => None, + }, + _ => None, + }; + let array = match _val_.get("array") { + Some(serde_json::Value::Array(array_val)) => { + let mut array_val_result: Vec = Vec::new(); + for array_val_element in array_val { + array_val_result.push(match Some(array_val_element) { + Some(serde_json::Value::Bool(array_val_element_val)) => { + array_val_element_val.to_owned() + } + _ => false, + }); + } + Some(array_val_result) + } + _ => None, + }; + let record = match _val_.get("record") { + Some(serde_json::Value::Object(record_val)) => { + let mut record_val_result: BTreeMap = BTreeMap::new(); + for (_key_, _value_) in record_val.into_iter() { + record_val_result.insert( + _key_.to_owned(), + match Some(_value_.to_owned()) { + Some(serde_json::Value::Bool(value_val)) => { + value_val.to_owned() + } + _ => false, + }, + ); + } + Some(record_val_result) + } + _ => None, + }; + let discriminator = match _val_.get("discriminator") { + Some(discriminator_val) => match discriminator_val { + serde_json::Value::Object(_) => { + Some(Discriminator::from_json(discriminator_val.to_owned())) + } + _ => None, + }, + _ => None, + }; + let any = match _val_.get("any") { + Some(any_val) => any_val.to_owned(), + _ => serde_json::Value::Null, + }; + + Self { + string, + boolean, + timestamp, + float32, + float64, + int8, + uint8, + int16, + uint16, + int32, + uint32, + int64, + uint64, + r#enum, + object, + array, + record, + discriminator, + any, + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + _json_output_.push_str("\"string\":"); + match &self.string { + Some(string_val) => { + _json_output_.push_str(serialize_string(string_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"boolean\":"); + match &self.boolean { + Some(boolean_val) => { + _json_output_.push_str(boolean_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"timestamp\":"); + match &self.timestamp { + Some(timestamp_val) => { + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"float32\":"); + match &self.float32 { + Some(float32_val) => { + _json_output_.push_str(float32_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"float64\":"); + match &self.float64 { + Some(float64_val) => { + _json_output_.push_str(float64_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"int8\":"); + match &self.int8 { + Some(int8_val) => { + _json_output_.push_str(int8_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"uint8\":"); + match &self.uint8 { + Some(uint8_val) => { + _json_output_.push_str(uint8_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"int16\":"); + match &self.int16 { + Some(int16_val) => { + _json_output_.push_str(int16_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"uint16\":"); + match &self.uint16 { + Some(uint16_val) => { + _json_output_.push_str(uint16_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"int32\":"); + match &self.int32 { + Some(int32_val) => { + _json_output_.push_str(int32_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"uint32\":"); + match &self.uint32 { + Some(uint32_val) => { + _json_output_.push_str(uint32_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"int64\":"); + match &self.int64 { + Some(int64_val) => { + _json_output_.push_str(format!("\"{}\"", int64_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"uint64\":"); + match &self.uint64 { + Some(uint64_val) => { + _json_output_.push_str(format!("\"{}\"", uint64_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"enum\":"); + match &self.r#enum { + Some(enum_val) => { + _json_output_.push_str(format!("\"{}\"", enum_val.serial_value()).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"object\":"); + match &self.object { + Some(object_val) => { + _json_output_.push_str(object_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"array\":"); + match &self.array { + Some(array_val) => { + _json_output_.push('['); + for (_index_, _element_) in array_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_string().as_str()); + } + _json_output_.push(']'); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"record\":"); + match &self.record { + Some(record_val) => { + _json_output_.push('{'); + for (_index_, (_key_, _value_)) in record_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(format!("\"{}\":", _key_).as_str()); + _json_output_.push_str(_value_.to_string().as_str()); + } + _json_output_.push('}'); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"discriminator\":"); + match &self.discriminator { + Some(discriminator_val) => { + _json_output_.push_str(discriminator_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"any\":"); + _json_output_.push_str( + serde_json::to_string(&self.any) + .unwrap_or("null".to_string()) + .as_str(), + ); + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self.string { + Some(string_val) => { + _query_parts_.push(format!("string={}", string_val)); + } + _ => { + _query_parts_.push("string=null".to_string()); + } + }; + match &self.boolean { + Some(boolean_val) => { + _query_parts_.push(format!("boolean={}", boolean_val)); + } + _ => { + _query_parts_.push("boolean=null".to_string()); + } + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => { + _query_parts_.push("timestamp=null".to_string()); + } + }; + match &self.float32 { + Some(float32_val) => { + _query_parts_.push(format!("float32={}", float32_val)); + } + _ => { + _query_parts_.push("float32=null".to_string()); + } + }; + match &self.float64 { + Some(float64_val) => { + _query_parts_.push(format!("float64={}", float64_val)); + } + _ => { + _query_parts_.push("float64=null".to_string()); + } + }; + match &self.int8 { + Some(int8_val) => { + _query_parts_.push(format!("int8={}", int8_val)); + } + _ => { + _query_parts_.push("int8=null".to_string()); + } + }; + match &self.uint8 { + Some(uint8_val) => { + _query_parts_.push(format!("uint8={}", uint8_val)); + } + _ => { + _query_parts_.push("uint8=null".to_string()); + } + }; + match &self.int16 { + Some(int16_val) => { + _query_parts_.push(format!("int16={}", int16_val)); + } + _ => { + _query_parts_.push("int16=null".to_string()); + } + }; + match &self.uint16 { + Some(uint16_val) => { + _query_parts_.push(format!("uint16={}", uint16_val)); + } + _ => { + _query_parts_.push("uint16=null".to_string()); + } + }; + match &self.int32 { + Some(int32_val) => { + _query_parts_.push(format!("int32={}", int32_val)); + } + _ => { + _query_parts_.push("int32=null".to_string()); + } + }; + match &self.uint32 { + Some(uint32_val) => { + _query_parts_.push(format!("uint32={}", uint32_val)); + } + _ => { + _query_parts_.push("uint32=null".to_string()); + } + }; + match &self.int64 { + Some(int64_val) => { + _query_parts_.push(format!("int64={}", int64_val)); + } + _ => { + _query_parts_.push("int64=null".to_string()); + } + }; + match &self.uint64 { + Some(uint64_val) => { + _query_parts_.push(format!("uint64={}", uint64_val)); + } + _ => { + _query_parts_.push("uint64=null".to_string()); + } + }; + match &self.r#enum { + Some(enum_val) => { + _query_parts_.push(format!("enum={}", enum_val.serial_value())); + } + _ => { + _query_parts_.push("enum=null".to_string()); + } + }; + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithNullableFields/object."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithNullableFields/array."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithNullableFields/record."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithNullableFields/discriminator."); + println!("[WARNING] cannot serialize any's to query params. Skipping field at /ObjectWithNullableFields/any."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RecursiveObject { + pub left: Option>, + pub right: Option>, +} + +impl ArriModel for RecursiveObject { + fn new() -> Self { + Self { + left: None, + right: None, + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let left = match _val_.get("left") { + Some(left_val) => match left_val { + serde_json::Value::Object(_) => { + Some(Box::new(RecursiveObject::from_json(left_val.to_owned()))) + } + _ => None, + }, + _ => None, + }; + let right = match _val_.get("right") { + Some(right_val) => match right_val { + serde_json::Value::Object(_) => { + Some(Box::new(RecursiveObject::from_json(right_val.to_owned()))) + } + _ => None, + }, + _ => None, + }; + Self { left, right } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + _json_output_.push_str("\"left\":"); + match &self.left { + Some(left_val) => { + _json_output_.push_str(left_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"right\":"); + match &self.right { + Some(right_val) => { + _json_output_.push_str(right_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /RecursiveObject/left."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /RecursiveObject/right."); + _query_parts_.join("&") + } +} diff --git a/languages/rust/rust-codegen-reference/src/main.rs b/languages/rust/rust-codegen-reference/src/main.rs new file mode 100644 index 00000000..1d94a179 --- /dev/null +++ b/languages/rust/rust-codegen-reference/src/main.rs @@ -0,0 +1,322 @@ +use std::collections::HashMap; + +use arri_client::{chrono::DateTime, reqwest, ArriClientConfig, ArriClientService}; +use example_client::{Book, ExampleClient}; + +mod example_client; + +fn get_headers() -> HashMap<&'static str, &'static str> { + let mut headers = HashMap::new(); + headers.insert("Authorization", "Bearer 12345"); + headers +} + +#[tokio::main] +async fn main() { + let config = ArriClientConfig { + http_client: reqwest::Client::new(), + base_url: "http://localhost:3000".to_string(), + headers: get_headers, + }; + let client = ExampleClient::create(&config); + let result = client + .books + .create_book(Book { + id: "1".to_string(), + name: "Tom Sawyer".to_string(), + created_at: DateTime::default(), + updated_at: DateTime::default(), + }) + .await; + println!("CREATE_BOOK_RESULT: {:?}", result); +} + +#[cfg(test)] +mod parsing_and_serialization_tests { + use crate::example_client::{ + Book, BookParams, Discriminator, Enumerator, NestedObject, ObjectWithEveryType, + ObjectWithNullableFields, ObjectWithOptionalFields, RecursiveObject, + }; + use arri_client::{ + chrono::{DateTime, FixedOffset}, + serde_json::{self, json}, + ArriModel, + }; + use std::{collections::BTreeMap, fs}; + + fn get_test_date() -> DateTime { + DateTime::parse_from_rfc3339("2001-01-01T16:00:00.000Z").unwrap_or(DateTime::default()) + } + + #[test] + fn book_test() { + let file_path: String = "../../../tests/test-files/Book.json".to_string(); + let file_contents: String = fs::read_to_string(file_path).unwrap(); + + let reference: Book = Book { + id: "1".to_string(), + name: "The Adventures of Tom Sawyer".to_string(), + created_at: get_test_date(), + updated_at: get_test_date(), + }; + + assert_eq!( + Book::from_json_string(file_contents.clone()), + reference.clone() + ); + assert_eq!(reference.to_json_string(), file_contents.clone()); + assert_eq!(reference.to_query_params_string(), "id=1&name=The Adventures of Tom Sawyer&createdAt=2001-01-01T16:00:00.000Z&updatedAt=2001-01-01T16:00:00.000Z") + } + + #[test] + fn book_params_test() { + let file_path = "../../../tests/test-files/BookParams.json".to_string(); + let file_contents = fs::read_to_string(file_path).unwrap(); + let reference = BookParams { + book_id: "1".to_string(), + }; + assert_eq!( + BookParams::from_json_string(file_contents.clone()), + reference.clone() + ); + assert_eq!(reference.to_json_string(), file_contents.clone()); + assert_eq!(reference.to_query_params_string(), "bookId=1"); + } + + #[test] + fn nested_object_no_special_chars_test() { + let file_content = + fs::read_to_string("../../../tests/test-files/NestedObject_NoSpecialChars.json") + .unwrap(); + let reference = NestedObject { + id: "1".to_string(), + content: "hello world".to_string(), + }; + assert_eq!( + NestedObject::from_json_string(file_content.clone()), + reference.clone() + ); + assert_eq!(reference.to_json_string(), file_content.clone()); + } + + #[test] + fn nested_object_special_chars_test() { + let file_path = "../../../tests/test-files/NestedObject_SpecialChars.json"; + let file_content = fs::read_to_string(file_path).unwrap(); + let reference = NestedObject { + id: "1".to_string(), + content: "double-quote: \" | backslash: \\ | backspace: \x08 | form-feed: \x0C | newline: \n | carriage-return: \r | tab: \t | unicode: \u{0000}".to_string(), + }; + assert_eq!( + NestedObject::from_json_string(file_content.clone()), + reference.clone() + ); + assert_eq!(reference.to_json_string(), file_content.clone()); + } + + #[test] + fn object_with_every_type_test() { + let file_path = "../../../tests/test-files/ObjectWithEveryType.json"; + let file_content = fs::read_to_string(file_path).unwrap(); + let mut record_val: BTreeMap = BTreeMap::new(); + record_val.insert("A".to_string(), true); + record_val.insert("B".to_string(), false); + let reference = ObjectWithEveryType { + string: "".to_string(), + boolean: false, + timestamp: get_test_date(), + float32: 1.5, + float64: 1.5, + int8: 1, + uint8: 1, + int16: 10, + uint16: 10, + int32: 100, + uint32: 100, + int64: 1000, + uint64: 1000, + r#enum: Enumerator::Baz, + object: NestedObject { + id: "1".to_string(), + content: "hello world".to_string(), + }, + array: vec![true, false, false], + record: record_val, + discriminator: Discriminator::C { + id: "".to_string(), + name: "".to_string(), + date: get_test_date(), + }, + any: serde_json::Value::String("hello world".to_string()), + }; + + assert_eq!( + ObjectWithEveryType::from_json_string(file_content.clone()), + reference.clone() + ); + assert_eq!(file_content.clone(), reference.to_json_string()); + } + + #[test] + fn object_with_optional_fields_test() { + let file_content = fs::read_to_string( + "../../../tests/test-files/ObjectWithOptionalFields_AllUndefined.json", + ) + .unwrap(); + let reference = ObjectWithOptionalFields::new(); + assert_eq!( + ObjectWithOptionalFields::from_json_string(file_content.clone()), + reference.clone() + ); + assert_eq!(reference.to_json_string(), file_content.clone()); + } + + #[test] + fn object_with_optional_fields_no_undefined_test() { + let file_content = fs::read_to_string( + "../../../tests/test-files/ObjectWithOptionalFields_NoUndefined.json", + ) + .unwrap(); + let mut record_val: BTreeMap = BTreeMap::new(); + record_val.insert("A".to_string(), true); + record_val.insert("B".to_string(), false); + let reference = ObjectWithOptionalFields { + string: Some("".to_string()), + boolean: Some(false), + timestamp: Some(get_test_date()), + float32: Some(1.5), + float64: Some(1.5), + int8: Some(1), + uint8: Some(1), + int16: Some(10), + uint16: Some(10), + int32: Some(100), + uint32: Some(100), + int64: Some(1000), + uint64: Some(1000), + r#enum: Some(Enumerator::Baz), + object: Some(NestedObject { + id: "1".to_string(), + content: "hello world".to_string(), + }), + array: Some(vec![true, false, false]), + record: Some(record_val), + discriminator: Some(Discriminator::C { + id: "".to_string(), + name: "".to_string(), + date: get_test_date(), + }), + any: Some(serde_json::Value::String("hello world".to_string())), + }; + + assert_eq!( + ObjectWithOptionalFields::from_json_string(file_content.clone()), + reference.clone() + ); + assert_eq!(reference.to_json_string(), file_content.clone()); + } + + #[test] + fn object_with_nullable_fields_all_null_test() { + let file_path = "../../../tests/test-files/ObjectWithNullableFields_AllNull.json"; + let file_content = fs::read_to_string(file_path).unwrap(); + let reference = ObjectWithNullableFields { + string: None, + boolean: None, + timestamp: None, + float32: None, + float64: None, + int8: None, + uint8: None, + int16: None, + uint16: None, + int32: None, + uint32: None, + int64: None, + uint64: None, + r#enum: None, + object: None, + array: None, + record: None, + discriminator: None, + any: serde_json::Value::Null, + }; + assert_eq!( + ObjectWithNullableFields::from_json_string(file_content.clone()), + reference.clone() + ); + assert_eq!(reference.to_json_string(), file_content.clone()); + } + + #[test] + fn object_with_nullable_fields_no_null_test() { + let file_path = "../../../tests/test-files/ObjectWithNullableFields_NoNull.json"; + let file_content = fs::read_to_string(file_path).unwrap(); + let mut record_val: BTreeMap = BTreeMap::new(); + record_val.insert("A".to_string(), true); + record_val.insert("B".to_string(), false); + let mut any_val: BTreeMap = BTreeMap::new(); + any_val.insert("message".to_string(), "hello world".to_string()); + let reference = ObjectWithNullableFields { + string: Some("".to_string()), + boolean: Some(true), + timestamp: Some(get_test_date()), + float32: Some(1.5), + float64: Some(1.5), + int8: Some(1), + uint8: Some(1), + int16: Some(10), + uint16: Some(10), + int32: Some(100), + uint32: Some(100), + int64: Some(1000), + uint64: Some(1000), + r#enum: Some(Enumerator::Baz), + object: Some(NestedObject { + id: "".to_string(), + content: "".to_string(), + }), + array: Some(vec![true, false, false]), + record: Some(record_val), + discriminator: Some(Discriminator::C { + id: "".to_string(), + name: "".to_string(), + date: get_test_date(), + }), + any: json!({"message": "hello world"}), + }; + assert_eq!( + ObjectWithNullableFields::from_json_string(file_content.clone()), + reference.clone() + ); + assert_eq!(reference.to_json_string(), file_content.clone()); + } + + #[test] + fn recursive_object_test() { + let file_path = "../../../tests/test-files/RecursiveObject.json"; + let file_content = fs::read_to_string(file_path).unwrap(); + let reference = RecursiveObject { + left: Some(Box::new(RecursiveObject { + left: Some(Box::new(RecursiveObject { + left: None, + right: Some(Box::new(RecursiveObject { + left: None, + right: None, + })), + })), + right: None, + })), + right: Some(Box::new(RecursiveObject { + left: None, + right: None, + })), + }; + assert_eq!( + RecursiveObject::from_json_string(file_content.clone()), + reference.clone() + ); + assert_eq!(file_content.clone(), reference.to_json_string()); + assert_eq!(reference.to_query_params_string(), "".to_string()); + } +} diff --git a/languages/rust/rust-codegen/.gitignore b/languages/rust/rust-codegen/.gitignore new file mode 100644 index 00000000..268cdab0 --- /dev/null +++ b/languages/rust/rust-codegen/.gitignore @@ -0,0 +1 @@ +.tmp \ No newline at end of file diff --git a/languages/rust/rust-codegen/README.md b/languages/rust/rust-codegen/README.md new file mode 100644 index 00000000..0a3efbeb --- /dev/null +++ b/languages/rust/rust-codegen/README.md @@ -0,0 +1,11 @@ +# arri-codegen-rust + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build arri-codegen-rust` to build the library. + +## Running unit tests + +Run `nx test arri-codegen-rust` to execute the unit tests via [Vitest](https://vitest.dev). diff --git a/languages/rust/rust-codegen/build.config.ts b/languages/rust/rust-codegen/build.config.ts new file mode 100644 index 00000000..f80143a0 --- /dev/null +++ b/languages/rust/rust-codegen/build.config.ts @@ -0,0 +1,28 @@ +import { readFileSync } from "node:fs"; +import path from "node:path"; + +import { defineBuildConfig } from "unbuild"; + +const packageJson = JSON.parse( + readFileSync(path.resolve(__dirname, "./package.json"), { + encoding: "utf-8", + }), +); + +// eslint-disable-next-line @typescript-eslint/no-unsafe-argument +const deps = Object.keys(packageJson.dependencies); + +export default defineBuildConfig({ + entries: [{ input: "./src/_index", name: "index" }], + rollup: { + emitCJS: true, + dts: { + respectExternal: false, + }, + }, + outDir: "dist", + clean: true, + declaration: true, + failOnWarn: true, + externals: deps, +}); diff --git a/languages/rust/rust-codegen/package.json b/languages/rust/rust-codegen/package.json new file mode 100644 index 00000000..5184f53c --- /dev/null +++ b/languages/rust/rust-codegen/package.json @@ -0,0 +1,22 @@ +{ + "name": "@arrirpc/codegen-rust", + "type": "module", + "license": "MIT", + "author": { "name": "joshmossas", "url": "https://github.com/joshmossas" }, + "bugs": { "url": "https://github.com/modiimedia/arri/issues" }, + "repository": { + "type": "git", + "url": "https://github.com/modiimedia/arri.git", + "directory": "languages/rust/rust-codegen" + }, + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": ["dist"], + "dependencies": { + "@arrirpc/codegen-utils": "workspace:*", + "pathe": "^1.1.2" + }, + "devDependencies": { "@arrirpc/schema": "workspace:*" }, + "version": "0.51.2" +} diff --git a/languages/rust/rust-codegen/project.json b/languages/rust/rust-codegen/project.json new file mode 100644 index 00000000..44a4afe2 --- /dev/null +++ b/languages/rust/rust-codegen/project.json @@ -0,0 +1,55 @@ +{ + "name": "rust-codegen", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "languages/rust/rust-codegen/src", + "projectType": "library", + "targets": { + "build": { + "executor": "nx:run-commands", + "outputs": ["{projectRoot}/dist"], + "options": { + "command": "unbuild", + "cwd": "languages/rust/rust-codegen" + } + }, + "publish": { + "executor": "nx:run-commands", + "options": { + "command": "pnpm publish", + "cwd": "languages/rust/rust-codegen" + }, + "dependsOn": ["build"] + }, + "lint": { + "executor": "nx:run-commands", + "options": { + "command": "pnpm eslint languages/rust/rust-codegen" + } + }, + "test": { + "executor": "nx:run-commands", + "inputs": [ + "{workspaceRoot}/coverage/languages/rust/rust-codegen-reference/src", + "{workspaceRoot}/coverage/languages/rust/rust-codegen/src" + ], + "outputs": ["{workspaceRoot}/coverage/languages/rust/rust-codegen"], + "options": { + "command": "vitest run --passWithNoTests --globals", + "cwd": "languages/rust/rust-codegen" + }, + "configurations": { + "watch": { + "command": "vitest watch --passWithNoTests --globals" + } + } + }, + "show-test-results": { + "executor": "nx:run-commands", + "options": { + "command": "vite preview --outDir .temp/reports", + "cwd": "languages/rust/rust-codegen" + } + } + }, + "tags": [] +} diff --git a/languages/rust/rust-codegen/src/_common.ts b/languages/rust/rust-codegen/src/_common.ts new file mode 100644 index 00000000..8a89d8d0 --- /dev/null +++ b/languages/rust/rust-codegen/src/_common.ts @@ -0,0 +1,152 @@ +import { + pascalCase, + removeDisallowedChars, + Schema, + snakeCase, +} from "@arrirpc/codegen-utils"; +import path from "pathe"; + +export interface GeneratorContext { + clientVersion: string; + clientName: string; + typeNamePrefix: string; + instancePath: string; + schemaPath: string; + generatedTypes: string[]; + discriminatorKey?: string; + discriminatorValue?: string; + isOptional?: boolean; +} + +export interface RustProperty { + typeName: string; + defaultValue: string; + isNullable: boolean; + fromJsonTemplate: (input: string, key: string) => string; + toJsonTemplate: (input: string, target: string) => string; + toQueryStringTemplate: ( + input: string, + key: string, + target: string, + ) => string; + content: string; +} + +const reservedKeywords = [ + "as", + "break", + "const", + "continue", + "crate", + "else", + "enum", + "extern", + "false", + "fn", + "for", + "if", + "impl", + "in", + "let", + "loop", + "match", + "mod", + "move", + "mut", + "pub", + "ref", + "return", + "self", + "Self", + "static", + "struct", + "super", + "trait", + "true", + "type", + "unsafe", + "use", + "where", + "while", + "async", + "await", + "dyn", + "abstract", + "become", + "box", + "do", + "final", + "macro", + "override", + "priv", + "typeof", + "unsized", + "virtual", + "yield", + "try", + "macro_rules", + "union", + "'static", + "dyn", +]; + +const illegalChars = ".!@#$%^&*()-+=\\][{}'\";?"; +const numberChars = "0123456789"; + +export function validRustIdentifier(key: string): string { + const output = removeDisallowedChars(snakeCase(key), illegalChars); + if (numberChars.includes(output.charAt(0))) { + return `r#_${output}`; + } + if (reservedKeywords.includes(output)) { + return `r#${output}`; + } + return output; +} + +export function validRustName(name: string): string { + const output = removeDisallowedChars( + pascalCase(name, { normalize: true }), + illegalChars, + ); + if (numberChars.includes(output.charAt(0))) { + return `r#_${output}`; + } + if (reservedKeywords.includes(output)) { + return `r#${output}`; + } + return output; +} + +export const tmpDir = path.resolve(__dirname, "../.temp"); + +export function outputIsOptionType( + schema: Schema, + context: GeneratorContext, +): boolean { + return schema.nullable === true || context.isOptional === true; +} + +export function getTypeName(schema: Schema, context: GeneratorContext): string { + if (schema.metadata?.id) { + return validRustName(schema.metadata.id); + } + if (context.discriminatorKey && context.discriminatorValue) { + const parts = context.instancePath.split("/"); + const name = validRustName( + `${parts.join("_")}_${context.discriminatorValue}`, + ); + return name; + } + return validRustName(context.instancePath.split("/").join("_")); +} + +export function formatDescriptionComment( + description: string, + leading = "", +): string { + return description + .split("\n") + .map((line) => `${leading}/// ${line}`) + .join("\n"); +} diff --git a/languages/rust/rust-codegen/src/_index.test.ts b/languages/rust/rust-codegen/src/_index.test.ts new file mode 100644 index 00000000..069f17ac --- /dev/null +++ b/languages/rust/rust-codegen/src/_index.test.ts @@ -0,0 +1,49 @@ +import { isAppDefinition, normalizeWhitespace } from "@arrirpc/codegen-utils"; +import { execSync } from "child_process"; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; +import path from "pathe"; + +import { tmpDir } from "./_common"; +import { createRustClient } from "./_index"; + +beforeAll(() => { + if (!existsSync(tmpDir)) { + mkdirSync(tmpDir); + } +}); + +test("Generated code matches codegen reference", async () => { + const appDef = JSON.parse( + readFileSync( + path.resolve( + __dirname, + "../../../../tests/test-files/AppDefinition.json", + ), + { encoding: "utf8" }, + ), + ) as unknown; + const referenceClient = readFileSync( + path.resolve( + __dirname, + "../../rust-codegen-reference/src/example_client.rs", + ), + "utf8", + ); + if (!isAppDefinition(appDef)) { + throw new Error("Error loading test AppDefinition.json"); + } + const result = createRustClient(appDef, { + clientName: "ExampleClient", + typeNamePrefix: "", + instancePath: "", + schemaPath: "", + generatedTypes: [], + }); + const outputFile = path.resolve(tmpDir, "example_client.g.rs"); + writeFileSync(outputFile, result); + execSync(`rustfmt ${outputFile} --edition 2021`, { stdio: "inherit" }); + const finalResult = readFileSync(outputFile, "utf8"); + expect(normalizeWhitespace(finalResult)).toBe( + normalizeWhitespace(referenceClient), + ); +}); diff --git a/languages/rust/rust-codegen/src/_index.ts b/languages/rust/rust-codegen/src/_index.ts new file mode 100644 index 00000000..ec95a54a --- /dev/null +++ b/languages/rust/rust-codegen/src/_index.ts @@ -0,0 +1,280 @@ +import { execSync } from "node:child_process"; +import fs from "node:fs"; + +import { + type AppDefinition, + defineGeneratorPlugin, + isRpcDefinition, + isSchemaFormDiscriminator, + isSchemaFormElements, + isSchemaFormEnum, + isSchemaFormProperties, + isSchemaFormRef, + isSchemaFormType, + isSchemaFormValues, + isServiceDefinition, + type Schema, + unflattenProcedures, +} from "@arrirpc/codegen-utils"; +import path from "pathe"; + +import { + GeneratorContext, + RustProperty, + validRustIdentifier, + validRustName, +} from "./_common"; +import rustAnyFromSchema from "./any"; +import rustArrayFromSchema from "./array"; +import { rustTaggedUnionFromSchema } from "./discriminator"; +import rustEnumFromSchema from "./enum"; +import rustObjectFromSchema from "./object"; +import { + rustBooleanFromSchema, + rustF32FromSchema, + rustF64FromSchema, + rustI8FromSchema, + rustI16FromSchema, + rustI32FromSchema, + rustI64FromSchema, + rustStringFromSchema, + rustTimestampFromSchema, + rustU8FromSchema, + rustU16FromSchema, + rustU32FromSchema, + rustU64FromSchema, +} from "./primitives"; +import { rustRpcFromSchema, rustServiceFromSchema } from "./procedures"; +import rustRecordFromSchema from "./record"; +import rustRefFromSchema from "./ref"; + +export interface RustClientGeneratorOptions { + clientName: string; + outputFile: string; + format?: boolean; + typePrefix?: string; +} + +export const rustClientGenerator = defineGeneratorPlugin( + (options: RustClientGeneratorOptions) => { + return { + generator(def) { + const context: GeneratorContext = { + clientVersion: def.info?.version ?? "", + clientName: options.clientName, + typeNamePrefix: options.typePrefix ?? "", + instancePath: "", + schemaPath: "", + generatedTypes: [], + }; + const client = createRustClient(def, { + ...context, + }); + const outputFile = path.resolve(options.outputFile); + fs.writeFileSync(outputFile, client); + const shouldFormat = options.format ?? true; + if (shouldFormat) { + try { + execSync(`rustfmt ${outputFile} --edition 2021`, { + stdio: "inherit", + }); + } catch (err) { + console.error(`Error formatting`, err); + } + } + }, + options, + }; + }, +); + +export function createRustClient( + def: AppDefinition, + context: Omit, +): string { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const services = unflattenProcedures(def.procedures); + const rpcParts: string[] = []; + const subServices: { name: string; key: string }[] = []; + const subServiceContent: string[] = []; + for (const key of Object.keys(services)) { + const subDef = services[key]; + if (isServiceDefinition(subDef)) { + const service = rustServiceFromSchema(subDef, { + clientVersion: def.info?.version ?? "", + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: key, + schemaPath: key, + generatedTypes: context.generatedTypes, + }); + if (service.content) { + subServices.push({ + key: validRustIdentifier(key), + name: service.name, + }); + subServiceContent.push(service.content); + } + continue; + } + if (isRpcDefinition(subDef)) { + const rpc = rustRpcFromSchema(subDef, { + clientVersion: def.info?.version ?? "", + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: key, + schemaPath: key, + generatedTypes: context.generatedTypes, + }); + if (rpc) { + rpcParts.push(rpc); + } + continue; + } + } + const modelParts: string[] = []; + for (const key of Object.keys(def.definitions)) { + const result = rustTypeFromSchema(def.definitions[key]!, { + ...context, + clientVersion: def.info?.version ?? "", + instancePath: key, + schemaPath: "", + }); + if (result.content) { + modelParts.push(result.content); + } + } + if (rpcParts.length === 0 && subServiceContent.length === 0) { + return `#![allow(dead_code, unused_imports, unused_variables, unconditional_recursion, deprecated)] +use arri_client::{ + chrono::{DateTime, FixedOffset}, + serde_json::{self}, + utils::{serialize_date_time, serialize_string}, + ArriEnum, ArriModel, +}; +use std::collections::BTreeMap; +${modelParts.join("\n\n")}`; + } + const clientName = validRustName(context.clientName); + return `#![allow(dead_code, unused_imports, unused_variables, unconditional_recursion, deprecated)] +use arri_client::{ + chrono::{DateTime, FixedOffset}, + parsed_arri_request, reqwest, serde_json, + utils::{serialize_date_time, serialize_string}, + ArriClientConfig, ArriClientService, ArriEnum, ArriModel, ArriParsedRequestOptions, + ArriServerError, EmptyArriModel, +}; +use std::collections::BTreeMap; + +pub struct ${clientName}<'a> { + config: &'a ArriClientConfig, +${subServices.map((service) => ` pub ${service.key}: ${service.name}<'a>,`).join("\n")} +} + +impl<'a> ArriClientService<'a> for ${clientName}<'a> { + fn create(config: &'a ArriClientConfig) -> Self { + Self { + config: &config, +${subServices.map((service) => ` ${service.key}: ${service.name}::create(config),`).join("\n")} + } + } +} + +impl ${clientName}<'_> { +${rpcParts.join("\n")} +} + +${subServiceContent.join("\n\n")} + +${modelParts.join("\n\n")}`; +} + +export function rustTypeFromSchema( + schema: Schema, + context: GeneratorContext, +): RustProperty { + if (isSchemaFormType(schema)) { + switch (schema.type) { + case "string": + return rustStringFromSchema(schema, context); + case "boolean": + return rustBooleanFromSchema(schema, context); + case "timestamp": + return rustTimestampFromSchema(schema, context); + case "float32": + return rustF32FromSchema(schema, context); + case "float64": + return rustF64FromSchema(schema, context); + case "int8": + return rustI8FromSchema(schema, context); + case "uint8": + return rustU8FromSchema(schema, context); + case "int16": + return rustI16FromSchema(schema, context); + case "uint16": + return rustU16FromSchema(schema, context); + case "int32": + return rustI32FromSchema(schema, context); + case "uint32": + return rustU32FromSchema(schema, context); + case "int64": + return rustI64FromSchema(schema, context); + case "uint64": + return rustU64FromSchema(schema, context); + default: + schema.type satisfies never; + throw new Error(`Unhandled schema type: "${schema.type}"`); + } + } + if (isSchemaFormProperties(schema)) { + return rustObjectFromSchema(schema, context); + } + if (isSchemaFormEnum(schema)) { + return rustEnumFromSchema(schema, context); + } + if (isSchemaFormElements(schema)) { + return rustArrayFromSchema(schema, context); + } + if (isSchemaFormValues(schema)) { + return rustRecordFromSchema(schema, context); + } + if (isSchemaFormDiscriminator(schema)) { + return rustTaggedUnionFromSchema(schema, context); + } + if (isSchemaFormRef(schema)) { + return rustRefFromSchema(schema, context); + } + return rustAnyFromSchema(schema, context); +} + +// export function rustServiceFromDef( +// key: string, +// schema: ServiceDefinition, +// context: GeneratorContext, +// ): string { +// // const serviceId = pascalCase(`${context.parentId ?? ""}_${key}`); +// // context.parentId = serviceId; +// // const rpcsParts: string[] = []; +// // const subServiceParts: string[] = []; +// // for (const key of Object.keys(schema)) { +// // const subSchema = schema[key]; +// // if (isServiceDefinition(subSchema)) { +// // const subService = rustServiceFromDef(key, subSchema, context); +// // subServiceParts.push(subService); +// // continue; +// // } +// // if (isRpcDefinition(subSchema)) { +// // const rpc = rustProcedureFromDef(key, subSchema, context); +// // rpcsParts.push(rpc); +// // } +// // } +// return ""; +// } + +// export function rustProcedureFromDef( +// key: string, +// schema: RpcDefinition, +// context: GeneratorContext, +// ): string { +// return ""; +// } diff --git a/languages/rust/rust-codegen/src/any.ts b/languages/rust/rust-codegen/src/any.ts new file mode 100644 index 00000000..61481ccb --- /dev/null +++ b/languages/rust/rust-codegen/src/any.ts @@ -0,0 +1,40 @@ +import { SchemaFormEmpty } from "@arrirpc/codegen-utils"; + +import { GeneratorContext, RustProperty, validRustIdentifier } from "./_common"; + +export default function rustAnyFromSchema( + schema: SchemaFormEmpty, + context: GeneratorContext, +): RustProperty { + return { + typeName: context.isOptional + ? `Option` + : "serde_json::Value", + defaultValue: context.isOptional ? `None` : `serde_json::Value::Null`, + isNullable: false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => Some(${innerKey}.to_owned()), + _ => None, + }`; + } + return `match ${input} { + Some(${innerKey}) => ${innerKey}.to_owned(), + _ => serde_json::Value::Null, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str( + serde_json::to_string(${input}) + .unwrap_or("null".to_string()) + .as_str(), + )`; + }, + toQueryStringTemplate() { + return `println!("[WARNING] cannot serialize any's to query params. Skipping field at ${context.instancePath}.")`; + }, + content: "", + }; +} diff --git a/languages/rust/rust-codegen/src/array.ts b/languages/rust/rust-codegen/src/array.ts new file mode 100644 index 00000000..e93a9874 --- /dev/null +++ b/languages/rust/rust-codegen/src/array.ts @@ -0,0 +1,83 @@ +import { SchemaFormElements } from "@arrirpc/codegen-utils"; + +import { + GeneratorContext, + outputIsOptionType, + RustProperty, + validRustIdentifier, +} from "./_common"; +import { rustTypeFromSchema } from "./_index"; + +export default function rustArrayFromSchema( + schema: SchemaFormElements, + context: GeneratorContext, +): RustProperty { + const innerType = rustTypeFromSchema(schema.elements, { + instancePath: `${context.instancePath}/element`, + schemaPath: `${context.schemaPath}/elements`, + clientVersion: context.clientVersion, + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + generatedTypes: context.generatedTypes, + }); + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType + ? `Option>` + : `Vec<${innerType.typeName}>`; + const defaultValue = isOptionType ? `None` : `Vec::new()`; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Array(${innerKey})) => { + let mut ${innerKey}_result: Vec<${innerType.typeName}> = Vec::new(); + for ${innerKey}_element in ${innerKey} { + ${innerKey}_result.push(${innerType.fromJsonTemplate(`Some(${innerKey}_element)`, `${innerKey}_element`)}); + } + Some(${innerKey}_result) + } + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Array(${innerKey})) => { + let mut ${innerKey}_result: Vec<${innerType.typeName}> = Vec::new(); + for ${innerKey}_element in ${innerKey} { + ${innerKey}_result.push(${innerType.fromJsonTemplate(`Some(${innerKey}_element)`, `${innerKey}_element`)}); + } + ${innerKey}_result + } + _ => Vec::new(), + }`; + }, + toJsonTemplate(input, target) { + let innerTypeToJson = innerType.toJsonTemplate(`_element_`, target); + if (innerType.isNullable) { + innerTypeToJson = `match _element_ { + Some(_element_val_) => { + ${innerType.toJsonTemplate(`_element_val_`, target)}; + }, + _ => { + ${target}.push_str("null"); + } + }`; + } + return `${target}.push('['); + for (_index_, _element_) in ${input}.iter().enumerate() { + if _index_ != 0 { + ${target}.push(','); + } + ${innerTypeToJson}; + } + ${target}.push(']')`; + }, + toQueryStringTemplate() { + return `println!("[WARNING] cannot serialize arrays to query params. Skipping field at ${context.instancePath}.")`; + }, + content: innerType.content, + }; +} diff --git a/languages/rust/rust-codegen/src/discriminator.ts b/languages/rust/rust-codegen/src/discriminator.ts new file mode 100644 index 00000000..ff90a1c0 --- /dev/null +++ b/languages/rust/rust-codegen/src/discriminator.ts @@ -0,0 +1,301 @@ +import { SchemaFormDiscriminator } from "@arrirpc/codegen-utils"; + +import { + formatDescriptionComment, + GeneratorContext, + getTypeName, + outputIsOptionType, + RustProperty, + validRustIdentifier, + validRustName, +} from "./_common"; +import { rustTypeFromSchema } from "./_index"; + +export function rustTaggedUnionFromSchema( + schema: SchemaFormDiscriminator, + context: GeneratorContext, +): RustProperty { + const enumName = `${context.typeNamePrefix}${getTypeName(schema, context)}`; + const isOptionType = outputIsOptionType(schema, context); + const defaultValue = isOptionType ? "None" : `${enumName}::new()`; + const result: RustProperty = { + typeName: isOptionType ? `Option<${enumName}>` : enumName, + defaultValue, + isNullable: isOptionType, + fromJsonTemplate(input: string, key: string) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(${innerKey}) => match ${innerKey} { + serde_json::Value::Object(_) => { + Some(${enumName}::from_json(${innerKey}.to_owned())) + } + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(${innerKey}) => match ${innerKey} { + serde_json::Value::Object(_) => { + ${enumName}::from_json(${innerKey}.to_owned()) + } + _ => ${enumName}::new(), + }, + _ => ${enumName}::new(), + }`; + }, + toJsonTemplate(input: string, target: string) { + return `${target}.push_str(${input}.to_json_string().as_str())`; + }, + toQueryStringTemplate() { + return `println!("[WARNING] cannot serialize nested objects to query params. Skipping field at ${context.instancePath}.")`; + }, + content: "", + }; + if (context.generatedTypes.includes(enumName)) { + return result; + } + const discriminatorKey = schema.discriminator; + const discriminatorValues = Object.keys(schema.mapping); + if (discriminatorValues.length === 0) + throw new Error( + `Discriminator schemas must have at least one subtype. Issue at ${context.schemaPath}.`, + ); + type EnumSubType = { + name: string; + properties: { + name: string; + defaultValue: string; + typeName: string; + isDeprecated: boolean; + description: string; + }[]; + isDeprecated: boolean; + description: string; + toJsonParts: string[]; + toQueryParts: string[]; + }; + const subTypes: EnumSubType[] = []; + const subTypeContent: string[] = []; + const discriminatorKeyProperty = validRustIdentifier(discriminatorKey); + const fromJsonParts: string[] = []; + for (const discriminatorValue of discriminatorValues) { + const subTypeName = validRustName(discriminatorValue); + const subSchema = schema.mapping[discriminatorValue]!; + const subType: EnumSubType = { + name: subTypeName, + properties: [], + toJsonParts: [], + toQueryParts: [], + isDeprecated: subSchema.metadata?.isDeprecated ?? false, + description: subSchema.metadata?.description ?? "", + }; + fromJsonParts.push(`"${discriminatorValue}" => {`); + const keyNames: string[] = []; + subType.toJsonParts.push( + `\t\t_json_output_.push_str("\\"${discriminatorKey}\\":\\"${discriminatorValue}\\"");`, + ); + subType.toQueryParts.push( + `_query_parts_.push(format!("${discriminatorKey}=${discriminatorValue}"));`, + ); + for (const key of Object.keys(subSchema.properties)) { + const keySchema = subSchema.properties[key]!; + const keyType = rustTypeFromSchema(keySchema, { + clientVersion: context.clientVersion, + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: `${context.instancePath}/${key}`, + schemaPath: `${context.schemaPath}/mapping/${key}`, + generatedTypes: context.generatedTypes, + discriminatorKey: discriminatorKey, + discriminatorValue: discriminatorValue, + }); + if (keyType.content) subTypeContent.push(keyType.content); + const keyName = validRustIdentifier(key); + subType.properties.push({ + name: keyName, + defaultValue: keyType.defaultValue, + typeName: keyType.typeName, + isDeprecated: keySchema.metadata?.isDeprecated ?? false, + description: keySchema.metadata?.description ?? "", + }); + keyNames.push(keyName); + fromJsonParts.push( + `let ${keyName} = ${keyType.fromJsonTemplate(`_val_.get("${key}")`, key)};`, + ); + subType.toJsonParts.push( + `\t\t_json_output_.push_str(",\\"${key}\\":");`, + ); + if (keyType.isNullable) { + const innerKey = validRustIdentifier(`${key}_val`); + subType.toJsonParts.push(`match ${keyName} { + Some(${innerKey}) => { + ${keyType.toJsonTemplate(innerKey, "_json_output_")}; + } + _ => { + _json_output_.push_str("null"); + } + };`); + } else { + subType.toJsonParts.push( + `${keyType.toJsonTemplate(keyName, "_json_output_")};`, + ); + } + subType.toQueryParts.push( + `${keyType.toQueryStringTemplate(keyName, key, "_query_parts_")};`, + ); + } + for (const key of Object.keys(subSchema.optionalProperties ?? {})) { + const keySchema = subSchema.optionalProperties![key]!; + const keyType = rustTypeFromSchema(keySchema, { + clientVersion: context.clientVersion, + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: `${context.instancePath}/key`, + schemaPath: `${context.schemaPath}/mapping/${key}`, + generatedTypes: context.generatedTypes, + discriminatorKey: discriminatorKey, + discriminatorValue: discriminatorValue, + }); + if (keyType.content) subTypeContent.push(keyType.content); + const keyName = validRustIdentifier(key); + subType.properties.push({ + name: keyName, + defaultValue: keyType.defaultValue, + typeName: keyType.typeName, + isDeprecated: keySchema.metadata?.isDeprecated ?? false, + description: keySchema.metadata?.description ?? "", + }); + keyNames.push(keyName); + fromJsonParts.push( + `let ${keyName} = ${keyType.fromJsonTemplate(`_val_.get("${key}")`, key)};`, + ); + subType.toJsonParts.push( + `\t\t_json_output_.push_str(",\\"${key}\\":");`, + ); + if (keyType.isNullable) { + const innerKey = validRustIdentifier(`${key}_val`); + subType.toJsonParts.push(`match ${keyName} { + Some(${innerKey}) => { + ${keyType.toJsonTemplate(innerKey, "_json_output_")}; + } + _ => { + _json_output_.push_str("null"); + } + };`); + } else { + subType.toJsonParts.push( + `${keyType.toJsonTemplate(keyName, "_json_output_")};`, + ); + } + } + fromJsonParts.push(`Self::${subTypeName} { + ${subType.properties.map((prop) => `${prop.name},`).join("\n")} + }`); + fromJsonParts.push(`}`); + subTypes.push(subType); + } + let leading = ""; + if (schema.metadata?.description) { + leading += formatDescriptionComment(schema.metadata.description); + leading += "\n"; + } + if (schema.metadata?.isDeprecated) { + leading += "#[deprecated]\n"; + } + result.content = `${leading}#[derive(Clone, Debug, PartialEq)] +pub enum ${enumName} { + ${subTypes + .map((type) => { + let leading = ""; + if (type.description) { + leading += formatDescriptionComment(type.description); + leading += "\n"; + } + if (type.isDeprecated) { + leading += "#[deprecated]\n"; + } + return `${leading}${type.name} { + ${type.properties + .map((prop) => { + let leading = ""; + if (prop.description) { + leading += formatDescriptionComment(prop.description); + leading += "\n"; + } + if (prop.isDeprecated) { + leading += "#[deprecated]\n"; + } + return `${leading}${prop.name}: ${prop.typeName},`; + }) + .join("\n")} + },`; + }) + .join("\n")} +} + +impl ArriModel for ${enumName} { + fn new() -> Self { + Self::${subTypes[0]!.name} { + ${subTypes[0]?.properties.map((prop) => `${prop.name}: ${prop.defaultValue},`).join("\n")} + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let ${discriminatorKeyProperty} = match _val_.get("${discriminatorKey}") { + Some(serde_json::Value::String(${discriminatorKeyProperty}_val)) => ${discriminatorKeyProperty}_val.to_owned(), + _ => "".to_string(), + }; + match ${discriminatorKeyProperty}.as_str() { + ${fromJsonParts.join("\n")} + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + ${subTypes.map( + ( + type, + ) => `Self::${type.name} { ${type.properties.map((prop) => `${prop.name},`).join("\n")}} => { + ${type.toJsonParts.join("\n")} + }`, + )} + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + ${subTypes.map( + ( + type, + ) => `Self::${type.name} { ${type.properties.map((prop) => `${prop.name},`).join("\n")}} => { + ${type.toQueryParts.join("\n")} + }`, + )} + } + _query_parts_.join("&") + } +} + +${subTypeContent.join("\n\n")}`; + context.generatedTypes.push(enumName); + return result; +} diff --git a/languages/rust/rust-codegen/src/enum.ts b/languages/rust/rust-codegen/src/enum.ts new file mode 100644 index 00000000..f273a778 --- /dev/null +++ b/languages/rust/rust-codegen/src/enum.ts @@ -0,0 +1,118 @@ +import { SchemaFormEnum } from "@arrirpc/codegen-utils"; + +import { + formatDescriptionComment, + GeneratorContext, + getTypeName, + outputIsOptionType, + RustProperty, + validRustIdentifier, + validRustName, +} from "./_common"; + +export default function rustEnumFromSchema( + schema: SchemaFormEnum, + context: GeneratorContext, +): RustProperty { + const enumName = `${context.typeNamePrefix}${getTypeName(schema, context)}`; + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? `Option<${enumName}>` : enumName; + const defaultValue = isOptionType ? "None" : `${enumName}::default()`; + const result: RustProperty = { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => { + Some(${enumName}::from_string(${innerKey}.to_owned())) + } + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => { + ${enumName}::from_string(${innerKey}.to_owned()) + } + _ => ${enumName}::default(), + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(format!("\\"{}\\"", ${input}.serial_value()).as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey}.serial_value())); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey}.serial_value())); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}.serial_value()))`; + }, + content: "", + }; + if (context.generatedTypes.includes(enumName)) { + return result; + } + let defaultEnumValue: string = ""; + const initializationParts: string[] = []; + const fromStringParts: string[] = []; + const serialValueParts: string[] = []; + for (let i = 0; i < schema.enum.length; i++) { + const val = schema.enum[i]!; + const valName = validRustName(val); + if (i === 0) { + defaultEnumValue = valName; + } + initializationParts.push(`\t${valName},`); + fromStringParts.push(`\t\t\t"${val}" => Self::${valName},`); + serialValueParts.push( + `\t\t\t${enumName}::${valName} => "${val}".to_string(),`, + ); + } + let leading = ""; + if (schema.metadata?.description) { + leading += `${formatDescriptionComment(schema.metadata.description)}\n`; + } + if (schema.metadata?.isDeprecated) { + leading += `#[deprecated]\n`; + } + result.content = `${leading}#[derive(Clone, Debug, PartialEq)] +pub enum ${enumName} { +${initializationParts.join("\n")} +} + +impl ArriEnum for ${enumName} { + fn default() -> Self { + ${enumName}::${defaultEnumValue} + } + fn from_string(input: String) -> Self { + match input.as_str() { +${fromStringParts.join("\n")} + _ => Self::default(), + } + } + fn serial_value(&self) -> String { + match &self { +${serialValueParts.join("\n")} + } + } +}`; + context.generatedTypes.push(enumName); + return result; +} diff --git a/languages/rust/rust-codegen/src/object.ts b/languages/rust/rust-codegen/src/object.ts new file mode 100644 index 00000000..4cb72c96 --- /dev/null +++ b/languages/rust/rust-codegen/src/object.ts @@ -0,0 +1,256 @@ +import { + isSchemaFormElements, + isSchemaFormValues, + SchemaFormProperties, +} from "@arrirpc/codegen-utils"; + +import { + formatDescriptionComment, + GeneratorContext, + getTypeName, + outputIsOptionType, + RustProperty, + validRustIdentifier, +} from "./_common"; +import { rustTypeFromSchema } from "./_index"; + +export default function rustObjectFromSchema( + schema: SchemaFormProperties, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const structName = `${context.typeNamePrefix}${getTypeName(schema, context)}`; + const typeName: string = isOptionType + ? `Option<${structName}>` + : structName; + const defaultValue = isOptionType ? `None` : `${structName}::new()`; + const result: RustProperty = { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(${innerKey}) => match ${innerKey} { + serde_json::Value::Object(_) => { + Some(${structName}::from_json(${innerKey}.to_owned())) + } + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(${innerKey}) => ${structName}::from_json(${innerKey}.to_owned()), + _ => ${structName}::new(), + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_json_string().as_str())`; + }, + toQueryStringTemplate() { + return `println!("[WARNING] cannot serialize nested objects to query params. Skipping field at ${context.instancePath}.")`; + }, + content: "", + }; + if (context.generatedTypes.includes(structName)) { + return result; + } + const fieldNames: string[] = []; + const fieldDeclarationParts: string[] = []; + const defaultParts: string[] = []; + const fromJsonParts: string[] = []; + const toJsonParts: string[] = []; + const toQueryParamParams: string[] = []; + const subContent: string[] = []; + const requiredKeys = Object.keys(schema.properties); + const optionalKeys = Object.keys(schema.optionalProperties ?? {}); + + const hasKeys = requiredKeys.length > 0; + for (let i = 0; i < requiredKeys.length; i++) { + const key = requiredKeys[i]!; + const prop = schema.properties[key]!; + const innerType = rustTypeFromSchema(prop, { + clientVersion: context.clientVersion, + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: `/${structName}/${key}`, + schemaPath: `${context.schemaPath}/properties/${key}`, + generatedTypes: context.generatedTypes, + }); + if (innerType.content) { + subContent.push(innerType.content); + } + const fieldName = validRustIdentifier(key); + fieldNames.push(fieldName); + let leading = ""; + if (prop.metadata?.description) { + leading += formatDescriptionComment(prop.metadata.description); + leading += "\n"; + } + if (prop.metadata?.isDeprecated) { + leading += `\t#[deprecated]\n`; + } + fieldDeclarationParts.push( + `${leading}\tpub ${fieldName}: ${innerType.typeName}`, + ); + defaultParts.push(`\t\t\t${fieldName}: ${innerType.defaultValue}`); + fromJsonParts.push( + `\t\t\t\tlet ${fieldName} = ${innerType.fromJsonTemplate(`_val_.get("${key}")`, key)};`, + ); + if (i === 0) { + toJsonParts.push(`\t\t_json_output_.push_str("\\"${key}\\":");`); + } else { + toJsonParts.push(`\t\t_json_output_.push_str(",\\"${key}\\":");`); + } + if (innerType.isNullable) { + const innerKey = validRustIdentifier(`${key}_val`); + toJsonParts.push(`\t\tmatch &self.${fieldName} { + Some(${innerKey}) => { + ${innerType.toJsonTemplate(innerKey, "_json_output_")}; + } + _ => { + _json_output_.push_str("null"); + } + };`); + } else { + const leading = + isSchemaFormElements(prop) || isSchemaFormValues(prop) + ? "" + : "&"; + toJsonParts.push( + `\t\t${innerType.toJsonTemplate(`${leading}self.${fieldName}`, "_json_output_")};`, + ); + } + + toQueryParamParams.push( + `\t\t${innerType.toQueryStringTemplate(`&self.${fieldName}`, key, "_query_parts_")};`, + ); + } + for (let i = 0; i < optionalKeys.length; i++) { + const key = optionalKeys[i]!; + const prop = schema.optionalProperties![key]!; + const innerType = rustTypeFromSchema(prop, { + clientVersion: context.clientVersion, + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: `/${structName}/${key}`, + schemaPath: `${context.schemaPath}/optionalProperties/${key}`, + generatedTypes: context.generatedTypes, + isOptional: true, + }); + if (innerType.content) { + subContent.push(innerType.content); + } + const fieldName = validRustIdentifier(key); + fieldNames.push(fieldName); + let leading = prop.metadata?.description + ? `${prop.metadata.description + .split("\n") + .map((line) => `\t/// ${line}`) + .join("\n")}\n` + : ""; + if (prop.metadata?.isDeprecated) { + leading += `\t#[deprecated]\n`; + } + fieldDeclarationParts.push( + `${leading}\tpub ${fieldName}: ${innerType.typeName}`, + ); + defaultParts.push(`\t\t\t${fieldName}: ${innerType.defaultValue}`); + fromJsonParts.push( + `\t\t\t\tlet ${fieldName} = ${innerType.fromJsonTemplate(`_val_.get("${key}")`, key)};`, + ); + + // NOT CORRECT YET + if (hasKeys) { + const innerKey = validRustIdentifier(`${key}_val`); + toJsonParts.push(`match &self.${fieldName} { + Some(${innerKey}) => { + _json_output_.push_str(",\\"${key}\\":"); + ${innerType.toJsonTemplate(innerKey, "_json_output_")} + }, + _ => {} + };`); + } else { + const innerKey = validRustIdentifier(`${key}_val`); + toJsonParts.push(`\t\tmatch &self.${fieldName} { + Some(${innerKey}) => { + ${ + i !== 0 + ? `if _has_keys_ { + _json_output_.push(','); + }` + : "" + } + _json_output_.push_str("\\"${key}\\":"); + ${innerType.toJsonTemplate(innerKey, "_json_output_")}; + ${i !== optionalKeys.length - 1 ? "_has_keys_ = true;" : ""} + } + _ => {} + };`); + } + toQueryParamParams.push( + `\t\t\t\t${innerType.toQueryStringTemplate(`&self.${fieldName}`, key, "_query_parts_")};`, + ); + } + context.generatedTypes.push(structName); + let selfDeclaration = `Self { + ${fieldNames.join(",\n\t\t\t\t")}, + }`; + if (fieldNames.length < 4) { + selfDeclaration = `Self { ${fieldNames.join(", ")} }`; + } + let leading = ""; + if (schema.metadata?.description) { + leading += `${schema.metadata.description + .split("\n") + .map((line) => `/// ${line}`) + .join("\n")}\n`; + } + if (schema.metadata?.isDeprecated) { + leading += `#[deprecated]\n`; + } + result.content = `${leading}#[derive(Clone, Debug, PartialEq)] +pub struct ${structName} { +${fieldDeclarationParts.join(",\n")}, +} + +impl ArriModel for ${structName} { + fn new() -> Self { + Self { +${defaultParts.join(",\n")}, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { +${fromJsonParts.join("\n")} + ${selfDeclaration} + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + ${!hasKeys ? `let mut _has_keys_ = false;` : ""} +${toJsonParts.join("\n")} + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); +${toQueryParamParams.join("\n")} + _query_parts_.join("&") + } +} + +${subContent.join("\n\n")}`; + return result; +} diff --git a/languages/rust/rust-codegen/src/primitives.ts b/languages/rust/rust-codegen/src/primitives.ts new file mode 100644 index 00000000..f5ff0009 --- /dev/null +++ b/languages/rust/rust-codegen/src/primitives.ts @@ -0,0 +1,784 @@ +import { SchemaFormType } from "@arrirpc/codegen-utils"; + +import { + GeneratorContext, + outputIsOptionType, + RustProperty, + validRustIdentifier, +} from "./_common"; + +export function rustStringFromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const defaultValue = isOptionType ? "None" : '"".to_string()'; + const typeName = isOptionType ? "Option" : "String"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => Some(${innerKey}.to_owned()), + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => ${innerKey}.to_owned(), + _ => "".to_string(), + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(serialize_string(${input}).as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustBooleanFromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const defaultValue = isOptionType ? "None" : "false"; + const typeName = isOptionType ? "Option" : "bool"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Bool(${innerKey})) => Some(${innerKey}.to_owned()), + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Bool(${innerKey})) => ${innerKey}.to_owned(), + _ => false, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustTimestampFromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType + ? "Option>" + : "DateTime"; + const defaultValue = isOptionType ? "None" : "DateTime::default()"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => { + match DateTime::::parse_from_rfc3339(${innerKey}) { + Ok(${innerKey}_result) => Some(${innerKey}_result), + Err(_) => None, + } + } + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => { + DateTime::::parse_from_rfc3339(${innerKey}) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(serialize_date_time(${input}, true).as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!( + "${key}={}", + serialize_date_time(${innerKey}, false) + )); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!( + "${key}={}", + serialize_date_time(${innerKey}, false) + )); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!( + "${key}={}", + serialize_date_time(${input}, false) + ))`; + }, + content: "", + }; +} + +export function rustF32FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? `Option` : "f32"; + const defaultValue = isOptionType ? `None` : "0.0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_f64() { + Some(${innerKey}_result) => Some(${innerKey}_result as f32), + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => { + ${innerKey}.as_f64().unwrap_or(0.0) as f32 + } + _ => 0.0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustF64FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? `Option` : "f64"; + const defaultValue = isOptionType ? `None` : "0.0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_f64() { + Some(${innerKey}_result) => Some(${innerKey}_result), + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => { + ${innerKey}.as_f64().unwrap_or(0.0) + } + _ => 0.0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustI8FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? `Option` : "i8"; + const defaultValue = isOptionType ? `None` : "0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_i64() { + Some(${innerKey}_result) => match i8::try_from(${innerKey}_result) { + Ok(${innerKey}_result_val) => Some(${innerKey}_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => { + i8::try_from(${innerKey}.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustU8FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? "Option" : "u8"; + const defaultValue = isOptionType ? "None" : "0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_u64() { + Some(${innerKey}_result) => match u8::try_from(${innerKey}_result) { + Ok(${innerKey}_result_val) => Some(${innerKey}_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => { + u8::try_from(${innerKey}.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustI16FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? "Option" : "i16"; + const defaultValue = isOptionType ? "None" : "0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_i64() { + Some(${innerKey}_result) => match i16::try_from(${innerKey}_result) { + Ok(${innerKey}_result_val) => Some(${innerKey}_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => { + i16::try_from(${innerKey}.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustU16FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? "Option" : "u16"; + const defaultValue = isOptionType ? "None" : "0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_u64() { + Some(${innerKey}_result) => match u16::try_from(${innerKey}_result) { + Ok(${innerKey}_result_val) => Some(${innerKey}_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => { + u16::try_from(${innerKey}.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustI32FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? "Option" : "i32"; + const defaultValue = isOptionType ? "None" : "0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_i64() { + Some(${innerKey}_result) => match i32::try_from(${innerKey}_result) { + Ok(${innerKey}_result_val) => Some(${innerKey}_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => { + i32::try_from(${innerKey}.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustU32FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? "Option" : "u32"; + const defaultValue = isOptionType ? "None" : "0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_u64() { + Some(${innerKey}_result) => match u32::try_from(${innerKey}_result) { + Ok(${innerKey}_result_val) => Some(${innerKey}_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Number(${innerKey})) => { + u32::try_from(${innerKey}.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_string().as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustI64FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? "Option" : "i64"; + const defaultValue = isOptionType ? "None" : "0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => match ${innerKey}.parse::() { + Ok(${innerKey}_result) => Some(${innerKey}_result), + Err(_) => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => { + ${innerKey}.parse::().unwrap_or(0) + } + _ => 0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(format!("\\"{}\\"", ${input}).as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} + +export function rustU64FromSchema( + schema: SchemaFormType, + context: GeneratorContext, +): RustProperty { + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType ? "Option" : "u64"; + const defaultValue = isOptionType ? "None" : "0"; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => match ${innerKey}.parse::() { + Ok(${innerKey}_result) => Some(${innerKey}_result), + Err(_) => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::String(${innerKey})) => { + ${innerKey}.parse::().unwrap_or(0) + } + _ => 0, + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(format!("\\"{}\\"", ${input}).as_str())`; + }, + toQueryStringTemplate(input, key, target) { + const innerKey = validRustIdentifier(`${key}_val`); + if (context.isOptional) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => {} + }`; + } + if (schema.nullable) { + return `match ${input} { + Some(${innerKey}) => { + ${target}.push(format!("${key}={}", ${innerKey})); + } + _ => { + ${target}.push("${key}=null".to_string()); + } + }`; + } + return `${target}.push(format!("${key}={}", ${input}))`; + }, + content: "", + }; +} diff --git a/languages/rust/rust-codegen/src/procedures.ts b/languages/rust/rust-codegen/src/procedures.ts new file mode 100644 index 00000000..863e09d5 --- /dev/null +++ b/languages/rust/rust-codegen/src/procedures.ts @@ -0,0 +1,170 @@ +import { + HttpRpcDefinition, + isRpcDefinition, + isServiceDefinition, + RpcDefinition, + ServiceDefinition, + WsRpcDefinition, +} from "@arrirpc/codegen-utils"; +import assert from "assert"; + +import { + formatDescriptionComment, + GeneratorContext, + validRustIdentifier, + validRustName, +} from "./_common"; + +export function rustRpcFromSchema( + schema: RpcDefinition, + context: GeneratorContext, +): string { + switch (schema.transport) { + case "http": + return rustHttpRpcFromSchema(schema, context); + case "ws": + return rustWsRpcFromSchema(schema, context); + default: + console.warn( + `[rust-codegen] Unknown transport type "${schema.transport}". Skipping ${context.instancePath}.`, + ); + return ""; + } +} + +export function rustHttpRpcFromSchema( + schema: HttpRpcDefinition, + context: GeneratorContext, +): string { + if (schema.isEventStream) { + console.warn( + `[rust-codegen] SSE is not supported at this time. Skipping ${context.instancePath}.`, + ); + return ""; + } + const functionName = getFunctionName(context.instancePath); + const params = schema.params ? validRustName(schema.params) : undefined; + const response = schema.response + ? validRustName(schema.response) + : undefined; + let leading = ""; + if (schema.description) { + leading += formatDescriptionComment(schema.description); + leading += "\n"; + } + if (schema.isDeprecated) { + leading += "#[deprecated]\n"; + } + return `${leading}pub async fn ${functionName} ( + self: &Self, + ${params ? `params: ${context.typeNamePrefix}${params},` : ""} + ) -> Result<${context.typeNamePrefix}${response ?? "()"}, ArriServerError> { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}${schema.path}", &self.config.base_url), + method: reqwest::Method::${schema.method.toUpperCase()}, + headers: self.config.headers, + client_version: "${context.clientVersion}".to_string(), + }, + ${params ? `Some(params)` : "None::"}, + |body| ${response ? `return ${context.typeNamePrefix}${response}::from_json_string(body)` : "{}"}, + ) + .await + }`; +} + +export function rustWsRpcFromSchema( + schema: WsRpcDefinition, + context: GeneratorContext, +): string { + console.warn( + `[rust-codegen] WS RPCs are not supported at this time. Skipping ${context.instancePath}.`, + ); + return ""; +} + +export function getFunctionName(instancePath: string): string { + assert(instancePath.length > 0); + const name = instancePath.split(".").pop() ?? ""; + return validRustIdentifier(name); +} + +export function getServiceName( + instancePath: string, + context: GeneratorContext, +): string { + assert(instancePath.length > 0); + const name = instancePath.split(".").join("_"); + return validRustName(`${context.clientName}_${name}_Service`); +} + +export function rustServiceFromSchema( + schema: ServiceDefinition, + context: GeneratorContext, +): { name: string; content: string } { + const serviceName = getServiceName(context.instancePath, context); + const subServices: { key: string; name: string }[] = []; + const subServiceContent: string[] = []; + const rpcParts: string[] = []; + for (const key of Object.keys(schema)) { + const subSchema = schema[key]!; + if (isServiceDefinition(subSchema)) { + const subService = rustServiceFromSchema(subSchema, { + clientVersion: context.clientVersion, + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: `${context.instancePath}.${key}`, + schemaPath: `${context.schemaPath}.${key}`, + generatedTypes: context.generatedTypes, + }); + if (subService.content) { + subServices.push({ + key: validRustIdentifier(key), + name: subService.name, + }); + } + continue; + } + if (isRpcDefinition(subSchema)) { + const rpc = rustRpcFromSchema(subSchema, { + clientVersion: context.clientVersion, + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: `${context.instancePath}.${key}`, + schemaPath: `${context.schemaPath}.${key}`, + generatedTypes: context.generatedTypes, + }); + if (rpc) { + rpcParts.push(rpc); + } + continue; + } + throw new Error( + `[rust-codegen] Invalid schema at /procedures/${context.instancePath}.`, + ); + } + return { + name: serviceName, + content: `pub struct ${serviceName}<'a> { + config: &'a ArriClientConfig, +${subServices.map((service) => ` pub ${service.key}: ${service.name}<'a>,`).join("\n")} +} + +impl<'a> ArriClientService<'a> for ${serviceName}<'a> { + fn create(config: &'a ArriClientConfig) -> Self { + Self { + config: &config, +${subServices.map((service) => ` ${service.key}: ${service.name}::create(config),`).join("\n")} + } + } +} + +impl ${serviceName}<'_> { +${rpcParts.join("\n")} +} + +${subServiceContent.join("\n\n")} +`, + }; +} diff --git a/languages/rust/rust-codegen/src/record.ts b/languages/rust/rust-codegen/src/record.ts new file mode 100644 index 00000000..904aed91 --- /dev/null +++ b/languages/rust/rust-codegen/src/record.ts @@ -0,0 +1,97 @@ +import { SchemaFormValues } from "@arrirpc/codegen-utils"; + +import { + GeneratorContext, + outputIsOptionType, + RustProperty, + validRustIdentifier, +} from "./_common"; +import { rustTypeFromSchema } from "./_index"; + +export default function rustRecordFromSchema( + schema: SchemaFormValues, + context: GeneratorContext, +): RustProperty { + const innerType = rustTypeFromSchema(schema.values, { + clientVersion: context.clientVersion, + clientName: context.clientName, + typeNamePrefix: context.typeNamePrefix, + instancePath: `${context.instancePath}/value`, + schemaPath: `${context.schemaPath}/values`, + generatedTypes: context.generatedTypes, + }); + const isOptionType = outputIsOptionType(schema, context); + const typeName = isOptionType + ? `Option>` + : `BTreeMap`; + const defaultValue = isOptionType ? `None` : `BTreeMap::new()`; + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + if (isOptionType) { + return `match ${input} { + Some(serde_json::Value::Object(${innerKey})) => { + let mut ${innerKey}_result: BTreeMap = BTreeMap::new(); + for (_key_, _value_) in ${innerKey}.into_iter() { + ${innerKey}_result.insert( + _key_.to_owned(), + ${innerType.fromJsonTemplate(`Some(_value_.to_owned())`, `value`)}, + ); + } + Some(${innerKey}_result) + } + _ => None, + }`; + } + return `match ${input} { + Some(serde_json::Value::Object(${innerKey})) => { + let mut ${innerKey}_result: BTreeMap = BTreeMap::new(); + for (_key_, _value_) in ${innerKey}.into_iter() { + ${innerKey}_result.insert( + _key_.to_owned(), + ${innerType.fromJsonTemplate(`Some(_value_.to_owned())`, `value`)}, + ); + } + ${innerKey}_result + } + _ => BTreeMap::new(), + }`; + }, + toJsonTemplate(input, target) { + if (innerType.isNullable) { + return `${target}.push('{'); + for (_index_, (_key_, _value_)) in ${input}.iter().enumerate() { + if _index_ != 0 { + ${target}.push(','); + } + ${target}.push_str(format!("\\"{}\\":", _key_).as_str()); + match _value_ { + Some(value_val) => { + ${innerType.toJsonTemplate("value_val", target)}; + }, + _ => { + ${target}.push_str("null"); + } + } + } + ${target}.push('}')`; + } + return `${target}.push('{'); + for (_index_, (_key_, _value_)) in ${input}.iter().enumerate() { + if _index_ != 0 { + ${target}.push(','); + } + ${target}.push_str(format!("\\"{}\\":", _key_).as_str()); + ${innerType.toJsonTemplate(`_value_`, target)}; + } + ${target}.push('}')`; + }, + toQueryStringTemplate() { + return `println!("[WARNING] cannot serialize nested objects to query params. Skipping field at ${context.instancePath}.")`; + }, + content: innerType.content, + }; +} diff --git a/languages/rust/rust-codegen/src/ref.ts b/languages/rust/rust-codegen/src/ref.ts new file mode 100644 index 00000000..a8be4c14 --- /dev/null +++ b/languages/rust/rust-codegen/src/ref.ts @@ -0,0 +1,71 @@ +import { SchemaFormRef } from "@arrirpc/codegen-utils"; + +import { + GeneratorContext, + outputIsOptionType, + RustProperty, + validRustIdentifier, + validRustName, +} from "./_common"; + +export default function rustRefFromSchema( + schema: SchemaFormRef, + context: GeneratorContext, +): RustProperty { + const innerTypeName = `${context.typeNamePrefix}${validRustName(schema.ref)}`; + const isOptionType = outputIsOptionType(schema, context); + const needsBoxing = true; + let typeName = `Box<${innerTypeName}>`; + if (isOptionType) { + typeName = `Option<${typeName}>`; + } + let defaultValue: string; + if (isOptionType) { + defaultValue = "None"; + } else if (needsBoxing) { + defaultValue = `Box::new(${innerTypeName}::new())`; + } else { + defaultValue = `${innerTypeName}::new()`; + } + return { + typeName, + defaultValue, + isNullable: schema.nullable ?? false, + fromJsonTemplate(input, key) { + const innerKey = validRustIdentifier(`${key}_val`); + const valFromJson = (input: string) => { + if (needsBoxing) { + return `Box::new(${innerTypeName}::from_json(${input}.to_owned()))`; + } + return `${innerTypeName}::from_json(${input}.to_owned())`; + }; + if (isOptionType) { + return `match ${input} { + Some(${innerKey}) => match ${innerKey} { + serde_json::Value::Object(_) => { + Some(${valFromJson(innerKey)}) + } + _ => None, + }, + _ => None, + }`; + } + return `match ${input} { + Some(${innerKey}) => match ${innerKey} { + serde_json::Value::Object(_) => { + ${valFromJson(innerKey)} + } + _ => ${valFromJson(innerKey)}, + }, + _ => Box::new(${innerTypeName}::new()), + }`; + }, + toJsonTemplate(input, target) { + return `${target}.push_str(${input}.to_json_string().as_str())`; + }, + toQueryStringTemplate() { + return `println!("[WARNING] cannot serialize nested objects to query params. Skipping field at ${context.instancePath}.")`; + }, + content: "", + }; +} diff --git a/languages/rust/rust-codegen/tsconfig.json b/languages/rust/rust-codegen/tsconfig.json new file mode 100644 index 00000000..c9f34274 --- /dev/null +++ b/languages/rust/rust-codegen/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "types": ["vitest"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/languages/rust/rust-codegen/tsconfig.lib.json b/languages/rust/rust-codegen/tsconfig.lib.json new file mode 100644 index 00000000..4befa7f0 --- /dev/null +++ b/languages/rust/rust-codegen/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/languages/rust/rust-codegen/tsconfig.spec.json b/languages/rust/rust-codegen/tsconfig.spec.json new file mode 100644 index 00000000..7f69b8dc --- /dev/null +++ b/languages/rust/rust-codegen/tsconfig.spec.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] + }, + "include": [ + "vite.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/languages/rust/rust-codegen/vite.config.ts b/languages/rust/rust-codegen/vite.config.ts new file mode 100644 index 00000000..ffafb1b7 --- /dev/null +++ b/languages/rust/rust-codegen/vite.config.ts @@ -0,0 +1,32 @@ +import viteTsConfigPaths from "vite-tsconfig-paths"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + cacheDir: "../../../node_modules/.vite/arri-codegen/rust", + + plugins: [ + viteTsConfigPaths({ + root: "../../../", + }) as any, + ], + + // Uncomment this if you are using workers. + // worker: { + // plugins: [ + // viteTsConfigPaths({ + // root: '../../', + // }), + // ], + // }, + + test: { + globals: true, + reporters: ["default", "html"], + outputFile: "./.temp/reports/index.html", + cache: { + dir: "../../../node_modules/.vitest", + }, + environment: "node", + include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + }, +}); diff --git a/languages/ts/ts-client/README.md b/languages/ts/ts-client/README.md index e0915149..9ac4360d 100644 --- a/languages/ts/ts-client/README.md +++ b/languages/ts/ts-client/README.md @@ -1,11 +1,3 @@ -# arri-client +# TS Arri Client -This library was generated with [Nx](https://nx.dev). - -## Building - -Run `nx build arri-client` to build the library. - -## Running unit tests - -Run `nx test arri-client` to execute the unit tests via [Jest](https://jestjs.io). +This library is needed to use the typescript code generated by [ARRI-RPC](https://github.com/modiimedia/arri) diff --git a/languages/ts/ts-client/package.json b/languages/ts/ts-client/package.json index 2e269ae6..ab665db8 100644 --- a/languages/ts/ts-client/package.json +++ b/languages/ts/ts-client/package.json @@ -5,17 +5,10 @@ "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", - "files": [ - "dist" - ], + "files": ["dist"], "license": "MIT", - "author": { - "name": "joshmossas", - "url": "https://github.com/joshmossas" - }, - "bugs": { - "url": "https://github.com/modiimedia/arri/issues" - }, + "author": { "name": "joshmossas", "url": "https://github.com/joshmossas" }, + "bugs": { "url": "https://github.com/modiimedia/arri/issues" }, "repository": { "type": "git", "url": "https://github.com/modiimedia/arri.git", @@ -27,7 +20,5 @@ "ofetch": "^1.3.4", "ws": "^8.17.1" }, - "devDependencies": { - "@types/ws": "^8.5.10" - } + "devDependencies": { "@types/ws": "^8.5.10" } } diff --git a/playground/src/app.ts b/playground/src/app.ts index 2bbce602..083faa68 100644 --- a/playground/src/app.ts +++ b/playground/src/app.ts @@ -1,7 +1,7 @@ import { ArriApp, handleCors } from "@arrirpc/server"; const app = new ArriApp({ - onRequest(event) { + async onRequest(event) { handleCors(event, { origin: "*", }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f504118..d9f2236f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,6 +132,19 @@ importers: specifier: workspace:* version: link:../../../tooling/json-schema-to-jtd + languages/rust/rust-codegen: + dependencies: + '@arrirpc/codegen-utils': + specifier: workspace:* + version: link:../../../tooling/codegen-utils + pathe: + specifier: ^1.1.2 + version: 1.1.2 + devDependencies: + '@arrirpc/schema': + specifier: workspace:* + version: link:../../../tooling/schema + languages/ts/ts-client: dependencies: '@arrirpc/schema': @@ -277,6 +290,9 @@ importers: '@arrirpc/codegen-kotlin': specifier: workspace:* version: link:../../languages/kotlin/kotlin-codegen + '@arrirpc/codegen-rust': + specifier: workspace:* + version: link:../../languages/rust/rust-codegen '@arrirpc/codegen-ts': specifier: workspace:* version: link:../../languages/ts/ts-codegen diff --git a/tests/clients/dart/lib/test_client.rpc.dart b/tests/clients/dart/lib/test_client.rpc.dart index 903b9f8c..76e52123 100644 --- a/tests/clients/dart/lib/test_client.rpc.dart +++ b/tests/clients/dart/lib/test_client.rpc.dart @@ -108,6 +108,18 @@ class TestClientTestsService { ); } + Future sendError(SendErrorParams params) async { + return parsedArriRequest( + "$_baseUrl/rpcs/tests/send-error", + method: HttpMethod.post, + httpClient: _httpClient, + headers: _headers, + clientVersion: _clientVersion, + params: params.toJson(), + parser: (body) {}, + ); + } + Future sendObject(ObjectWithEveryType params) async { return parsedArriRequest( "$_baseUrl/rpcs/tests/send-object", @@ -928,6 +940,88 @@ class DeprecatedRpcParams implements ArriModel { } } +class SendErrorParams implements ArriModel { + final int code; + final String message; + const SendErrorParams({ + required this.code, + required this.message, + }); + + factory SendErrorParams.empty() { + return SendErrorParams( + code: 0, + message: "", + ); + } + + factory SendErrorParams.fromJson(Map _input_) { + final code = intFromDynamic(_input_["code"], 0); + final message = typeFromDynamic(_input_["message"], ""); + return SendErrorParams( + code: code, + message: message, + ); + } + + factory SendErrorParams.fromJsonString(String input) { + return SendErrorParams.fromJson(json.decode(input)); + } + + @override + Map toJson() { + final _output_ = { + "code": code, + "message": message, + }; + + return _output_; + } + + @override + String toJsonString() { + return json.encode(toJson()); + } + + @override + String toUrlQueryParams() { + final _queryParts_ = []; + _queryParts_.add("code=$code"); + _queryParts_.add("message=$message"); + return _queryParts_.join("&"); + } + + @override + SendErrorParams copyWith({ + int? code, + String? message, + }) { + return SendErrorParams( + code: code ?? this.code, + message: message ?? this.message, + ); + } + + @override + List get props => [ + code, + message, + ]; + + @override + bool operator ==(Object other) { + return other is SendErrorParams && listsAreEqual(props, other.props); + } + + @override + int get hashCode => listToHashCode(props); + + @override + String toString() { + return "SendErrorParams ${toJsonString()}"; + } +} + class ObjectWithEveryType implements ArriModel { final dynamic any; final bool boolean; diff --git a/tests/clients/kotlin/src/main/kotlin/TestClient.rpc.kt b/tests/clients/kotlin/src/main/kotlin/TestClient.rpc.kt index 3d99b0a0..c911abbd 100644 --- a/tests/clients/kotlin/src/main/kotlin/TestClient.rpc.kt +++ b/tests/clients/kotlin/src/main/kotlin/TestClient.rpc.kt @@ -150,6 +150,21 @@ class TestClientTestsService( throw TestClientError.fromJson(response.bodyAsText()) } + suspend fun sendError(params: SendErrorParams): Unit { + val response = __prepareRequest( + client = httpClient, + url = "$baseUrl/rpcs/tests/send-error", + method = HttpMethod.Post, + params = params, + headers = headers?.invoke(), + ).execute() + + if (response.status.value in 200..299) { + return + } + throw TestClientError.fromJson(response.bodyAsText()) + } + suspend fun sendObject(params: ObjectWithEveryType): ObjectWithEveryType { val response = __prepareRequest( client = httpClient, @@ -1047,6 +1062,65 @@ val deprecatedField: String = when (__input.jsonObject["deprecatedField"]) { +data class SendErrorParams( + val code: UShort, + val message: String, +) : TestClientModel { + override fun toJson(): String { +var output = "{" +output += "\"code\":" +output += code +output += ",\"message\":" +output += buildString { printQuoted(message) } +output += "}" +return output + } + + override fun toUrlQueryParams(): String { +val queryParts = mutableListOf() +queryParts.add("code=$code") +queryParts.add("message=$message") +return queryParts.joinToString("&") + } + + companion object Factory : TestClientModelFactory { + @JvmStatic + override fun new(): SendErrorParams { + return SendErrorParams( + code = 0u, + message = "", + ) + } + + @JvmStatic + override fun fromJson(input: String): SendErrorParams { + return fromJsonElement(JsonInstance.parseToJsonElement(input)) + } + + @JvmStatic + override fun fromJsonElement(__input: JsonElement, instancePath: String): SendErrorParams { + if (__input !is JsonObject) { + __logError("[WARNING] SendErrorParams.fromJsonElement() expected kotlinx.serialization.json.JsonObject at $instancePath. Got ${__input.javaClass}. Initializing empty SendErrorParams.") + return new() + } +val code: UShort = when (__input.jsonObject["code"]) { + is JsonPrimitive -> __input.jsonObject["code"]!!.jsonPrimitive.contentOrNull?.toUShortOrNull() ?: 0u + else -> 0u + } +val message: String = when (__input.jsonObject["message"]) { + is JsonPrimitive -> __input.jsonObject["message"]!!.jsonPrimitive.contentOrNull ?: "" + else -> "" + } + return SendErrorParams( + code, + message, + ) + } + } +} + + + data class ObjectWithEveryType( val any: JsonElement, val boolean: Boolean, diff --git a/tests/clients/rust/Cargo.lock b/tests/clients/rust/Cargo.lock new file mode 100644 index 00000000..3e29bdc4 --- /dev/null +++ b/tests/clients/rust/Cargo.lock @@ -0,0 +1,1358 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arri_client" +version = "0.51.2" +dependencies = [ + "async-trait", + "chrono", + "regex", + "reqwest", + "serde_json", +] + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.4", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.4", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.4.2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eea5a9eb898d3783f17c6407670e3592fd174cb81a10e51d4c37f49450b9946" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "arri_client", + "tokio", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" +dependencies = [ + "bitflags 2.4.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/tests/clients/rust/Cargo.toml b/tests/clients/rust/Cargo.toml new file mode 100644 index 00000000..f7999f3c --- /dev/null +++ b/tests/clients/rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +arri_client = { path = "../../../languages/rust/rust-client"} +tokio = { version = "1", features = ["full"] } + diff --git a/tests/clients/rust/project.json b/tests/clients/rust/project.json new file mode 100644 index 00000000..0a2380c4 --- /dev/null +++ b/tests/clients/rust/project.json @@ -0,0 +1,21 @@ +{ + "name": "test-client-rust", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "implicitDependencies": ["test-server", "rust-client"], + "targets": { + "integration-test": { + "executor": "nx:run-commands", + "options": { + "command": "cargo test -- --nocapture", + "cwd": "tests/clients/rust" + } + }, + "cargo": { + "executor": "nx:run-commands", + "options": { + "command": "cargo", + "cwd": "tests/clients/rust" + } + } + } +} diff --git a/tests/clients/rust/src/main.rs b/tests/clients/rust/src/main.rs new file mode 100644 index 00000000..0ccb289b --- /dev/null +++ b/tests/clients/rust/src/main.rs @@ -0,0 +1,442 @@ +#[path = "test_client.g.rs"] +mod test_client; +fn main() {} + +#[cfg(test)] +mod tests { + use arri_client::{ + chrono::{DateTime, Utc}, + reqwest, serde_json, ArriClientConfig, ArriClientService, + }; + use std::collections::{BTreeMap, HashMap}; + + #[allow(deprecated)] + use crate::test_client::{ + DefaultPayload, DeprecatedRpcParams, ObjectWithEveryNullableType, + ObjectWithEveryNullableTypeNestedArrayElementElement, + ObjectWithEveryNullableTypeNestedObject, ObjectWithEveryNullableTypeNestedObjectData, + ObjectWithEveryNullableTypeNestedObjectDataData, ObjectWithEveryNullableTypeObject, + ObjectWithEveryOptionalType, ObjectWithEveryOptionalTypeNestedArrayElementElement, + ObjectWithEveryOptionalTypeNestedObject, ObjectWithEveryOptionalTypeNestedObjectData, + ObjectWithEveryOptionalTypeNestedObjectDataData, ObjectWithEveryType, + ObjectWithEveryTypeNestedArrayElementElement, ObjectWithEveryTypeNestedObject, + ObjectWithEveryTypeNestedObjectData, ObjectWithEveryTypeNestedObjectDataData, + ObjectWithEveryTypeObject, RecursiveObject, RecursiveUnion, RecursiveUnionDataShape, + SendErrorParams, TestClient, + }; + + const TARGET_MS: i64 = 978328800000; + + fn headers() -> HashMap<&'static str, &'static str> { + let mut result: HashMap<&'static str, &'static str> = HashMap::new(); + result.insert("x-test-header", "rust-12345"); + result + } + fn get_config(headers: fn() -> HashMap<&'static str, &'static str>) -> ArriClientConfig { + ArriClientConfig { + http_client: reqwest::Client::new(), + base_url: "http://127.0.0.1:2020".to_string(), + headers: headers, + } + } + + #[tokio::test] + async fn can_send_and_receive_objects() { + let config = get_config(headers); + let client = TestClient::create(&config); + let target_date = DateTime::::from_timestamp_millis(TARGET_MS).unwrap(); + let mut record = BTreeMap::::new(); + record.insert("A".to_string(), true); + record.insert("B".to_string(), false); + let mut input = ObjectWithEveryType { + any: serde_json::Value::String("hello world".to_string()), + boolean: true, + string: "hello world".to_string(), + timestamp: target_date.fixed_offset(), + float32: 1.5, + float64: 1.5, + int8: 1, + uint8: 1, + int16: 10, + uint16: 10, + int32: 100, + uint32: 100, + int64: 1000, + uint64: 1000, + enumerator: crate::test_client::ObjectWithEveryTypeEnumerator::C, + array: vec![true, false, true], + object: ObjectWithEveryTypeObject { + string: "hello world".to_string(), + boolean: true, + timestamp: target_date.fixed_offset(), + }, + record, + discriminator: crate::test_client::ObjectWithEveryTypeDiscriminator::A { + title: "hello world (A)".to_string(), + }, + nested_object: ObjectWithEveryTypeNestedObject { + id: "12345".to_string(), + timestamp: target_date.fixed_offset(), + data: ObjectWithEveryTypeNestedObjectData { + id: "123456".to_string(), + timestamp: target_date.fixed_offset(), + data: ObjectWithEveryTypeNestedObjectDataData { + id: "1234567".to_string(), + timestamp: target_date.fixed_offset(), + }, + }, + }, + nested_array: vec![vec![ + ObjectWithEveryTypeNestedArrayElementElement { + id: "1A".to_string(), + timestamp: target_date.fixed_offset(), + }, + ObjectWithEveryTypeNestedArrayElementElement { + id: "2A".to_string(), + timestamp: target_date.fixed_offset(), + }, + ObjectWithEveryTypeNestedArrayElementElement { + id: "1B".to_string(), + timestamp: target_date.fixed_offset(), + }, + ]], + }; + let result = client.tests.send_object(input.clone()).await; + assert_eq!(result.as_ref().unwrap(), &input.clone()); + input.nested_object.data.data.id = "different value".to_string(); + assert_ne!(result.as_ref().unwrap(), &input); + } + + #[tokio::test] + async fn unauthenticated_client_returns_error() { + let config = get_config(|| return HashMap::new()); + let client = TestClient::create(&config); + let result = client + .tests + .send_partial_object(ObjectWithEveryOptionalType { + any: None, + boolean: None, + string: None, + timestamp: None, + float32: None, + float64: None, + int8: None, + uint8: None, + int16: None, + uint16: None, + int32: None, + uint32: None, + int64: None, + uint64: None, + enumerator: None, + array: None, + object: None, + record: None, + discriminator: None, + nested_object: None, + nested_array: None, + }) + .await; + assert_eq!(result.is_err(), true); + assert_eq!(result.unwrap_err().code, 401); + } + + #[tokio::test] + async fn can_send_and_receive_object_with_nullable_fields() { + let config = get_config(headers); + let target_date = DateTime::from_timestamp_millis(TARGET_MS).unwrap(); + let client = TestClient::create(&config); + let all_null_input = ObjectWithEveryNullableType { + any: serde_json::Value::Null, + boolean: None, + string: None, + timestamp: None, + float32: None, + float64: None, + int8: None, + uint8: None, + int16: None, + uint16: None, + int32: None, + uint32: None, + int64: None, + uint64: None, + enumerator: None, + array: None, + object: None, + record: None, + discriminator: None, + nested_object: None, + nested_array: None, + }; + let mut record = BTreeMap::>::new(); + record.insert("A".to_string(), Some(true)); + record.insert("B".to_string(), Some(false)); + let mut no_null_input = ObjectWithEveryNullableType { + any: serde_json::Value::Bool(true), + boolean: Some(false), + string: Some("hello world".to_string()), + timestamp: Some(target_date.fixed_offset()), + float32: Some(1.5), + float64: Some(1.5), + int8: Some(1), + uint8: Some(1), + int16: Some(10), + uint16: Some(10), + int32: Some(100), + uint32: Some(100), + int64: Some(1000), + uint64: Some(1000), + enumerator: Some(crate::test_client::ObjectWithEveryNullableTypeEnumerator::B), + array: Some(vec![Some(true), Some(false), Some(true)]), + object: Some(ObjectWithEveryNullableTypeObject { + string: Some("hello world".to_string()), + boolean: Some(true), + timestamp: Some(target_date.fixed_offset()), + }), + record: Some(record), + discriminator: Some( + crate::test_client::ObjectWithEveryNullableTypeDiscriminator::B { + title: Some("hello world".to_string()), + description: Some("hello world again".to_string()), + }, + ), + nested_object: Some(ObjectWithEveryNullableTypeNestedObject { + id: Some("1".to_string()), + timestamp: Some(target_date.fixed_offset()), + data: Some(ObjectWithEveryNullableTypeNestedObjectData { + id: Some("2".to_string()), + timestamp: Some(target_date.fixed_offset()), + data: Some(ObjectWithEveryNullableTypeNestedObjectDataData { + id: Some("3".to_string()), + timestamp: Some(target_date.fixed_offset()), + }), + }), + }), + nested_array: Some(vec![Some(vec![ + Some(ObjectWithEveryNullableTypeNestedArrayElementElement { + id: Some("1".to_string()), + timestamp: Some(target_date.fixed_offset()), + }), + Some(ObjectWithEveryNullableTypeNestedArrayElementElement { + id: Some("2".to_string()), + timestamp: Some(target_date.fixed_offset()), + }), + ])]), + }; + let all_null_result = client + .tests + .send_object_with_nullable_fields(all_null_input.clone()) + .await; + assert_eq!( + all_null_result.as_ref().unwrap().clone(), + all_null_input.clone() + ); + let no_null_result = client + .tests + .send_object_with_nullable_fields(no_null_input.clone()) + .await; + assert_eq!( + no_null_result.as_ref().unwrap().clone(), + no_null_input.clone() + ); + no_null_input.nested_array = Some(vec![Some(vec![Some( + ObjectWithEveryNullableTypeNestedArrayElementElement { + id: Some("4".to_string()), + timestamp: None, + }, + )])]); + assert_ne!( + no_null_result.as_ref().unwrap().clone(), + no_null_input.clone() + ); + let new_result = client + .tests + .send_object_with_nullable_fields(no_null_input.clone()) + .await; + assert_eq!(new_result.as_ref().unwrap().clone(), no_null_input); + } + + #[tokio::test] + async fn can_send_and_receive_recursive_objects() { + let config = get_config(headers); + let client = TestClient::create(&config); + let input = RecursiveObject { + left: Some(Box::new(RecursiveObject { + left: Some(Box::new(RecursiveObject { + left: None, + right: None, + value: "d3".to_string(), + })), + right: Some(Box::new(RecursiveObject { + left: None, + right: Some(Box::new(RecursiveObject { + left: None, + right: None, + value: "d4".to_string(), + })), + value: "d3".to_string(), + })), + value: "d2".to_string(), + })), + right: Some(Box::new(RecursiveObject { + left: None, + right: None, + value: "d2".to_string(), + })), + value: "d1".to_string(), + }; + let result = client.tests.send_recursive_object(input.clone()).await; + assert_eq!(result.unwrap(), input); + } + + #[tokio::test] + async fn can_send_and_receive_recursive_discriminators() { + let config = get_config(headers); + let client = TestClient::create(&config); + let input = RecursiveUnion::Children { + data: vec![ + Box::new(RecursiveUnion::Child { + data: Box::new(RecursiveUnion::Text { + data: "Hello world".to_string(), + }), + }), + Box::new(RecursiveUnion::Shape { + data: RecursiveUnionDataShape { + width: 100.0, + height: 1000.0, + color: "#F6F6F6".to_string(), + }, + }), + Box::new(RecursiveUnion::Children { + data: vec![ + Box::new(RecursiveUnion::Text { + data: "Hello world".to_string(), + }), + Box::new(RecursiveUnion::Text { + data: "Hello world".to_string(), + }), + ], + }), + ], + }; + + let result = client.tests.send_recursive_union(input.clone()).await; + assert_eq!(result.unwrap(), input); + } + + #[tokio::test] + async fn can_send_requests_with_no_params() { + let config = get_config(headers); + let client = TestClient::create(&config); + let get_request_result = client.tests.empty_params_get_request().await; + let post_request_result = client.tests.empty_params_post_request().await; + assert!(!get_request_result.unwrap().message.is_empty()); + assert!(!post_request_result.unwrap().message.is_empty()); + } + + #[tokio::test] + async fn can_send_requests_with_no_response() { + let config = get_config(headers); + let client = TestClient::create(&config); + let get_request_result = client + .tests + .empty_response_get_request(DefaultPayload { + message: "hello world".to_string(), + }) + .await; + let post_request_result = client + .tests + .empty_response_post_request(DefaultPayload { + message: "hello world again".to_string(), + }) + .await; + assert!(get_request_result.is_ok()); + assert!(post_request_result.is_ok()); + } + + #[tokio::test] + async fn can_properly_parse_error_responses() { + let config = get_config(headers); + let client = TestClient::create(&config); + let result = client + .tests + .send_error(SendErrorParams { + code: 444, + message: "This is an error".to_string(), + }) + .await; + assert!(result.is_err()); + assert_eq!(result.as_ref().unwrap_err().code, 444); + assert_eq!(result.unwrap_err().message, "This is an error".to_string()); + } + + #[tokio::test] + async fn can_send_and_receive_partial_objects() { + let config = get_config(headers); + let client = TestClient::create(&config); + let target_date = DateTime::from_timestamp_millis(TARGET_MS).unwrap(); + let mut record = BTreeMap::::new(); + record.insert("A".to_string(), false); + record.insert("B".to_string(), true); + let input = ObjectWithEveryOptionalType { + any: None, + boolean: None, + string: Some("hello world".to_string()), + timestamp: Some(target_date.fixed_offset()), + float32: None, + float64: None, + int8: None, + uint8: None, + int16: None, + uint16: None, + int32: None, + uint32: None, + int64: None, + uint64: None, + enumerator: Some(crate::test_client::ObjectWithEveryOptionalTypeEnumerator::C), + array: None, + object: None, + record: Some(record), + discriminator: Some( + crate::test_client::ObjectWithEveryOptionalTypeDiscriminator::A { + title: "hello world".to_string(), + }, + ), + nested_object: Some(ObjectWithEveryOptionalTypeNestedObject { + id: "hello world".to_string(), + timestamp: target_date.fixed_offset(), + data: ObjectWithEveryOptionalTypeNestedObjectData { + id: "hello world".to_string(), + timestamp: target_date.fixed_offset(), + data: ObjectWithEveryOptionalTypeNestedObjectDataData { + id: "hello world".to_string(), + timestamp: target_date.fixed_offset(), + }, + }, + }), + nested_array: Some(vec![vec![ + ObjectWithEveryOptionalTypeNestedArrayElementElement { + id: "Hello world".to_string(), + timestamp: target_date.fixed_offset(), + }, + ]]), + }; + let result = client.tests.send_partial_object(input.clone()).await; + assert_eq!(result.unwrap(), input); + } + + #[tokio::test] + async fn deprecated_types_and_procedures_are_properly_marked() { + let config = get_config(headers); + let client = TestClient::create(&config); + #[allow(deprecated)] + let _ = client + .tests + .deprecated_rpc(DeprecatedRpcParams { + deprecated_field: "hello world".to_string(), + }) + .await; + assert!(true); + } +} diff --git a/tests/clients/rust/src/test_client.g.rs b/tests/clients/rust/src/test_client.g.rs new file mode 100644 index 00000000..4212af3f --- /dev/null +++ b/tests/clients/rust/src/test_client.g.rs @@ -0,0 +1,6006 @@ +#![allow( + dead_code, + unused_imports, + unused_variables, + unconditional_recursion, + deprecated +)] +use arri_client::{ + chrono::{DateTime, FixedOffset}, + parsed_arri_request, reqwest, serde_json, + utils::{serialize_date_time, serialize_string}, + ArriClientConfig, ArriClientService, ArriEnum, ArriModel, ArriParsedRequestOptions, + ArriServerError, EmptyArriModel, +}; +use std::collections::BTreeMap; + +pub struct TestClient<'a> { + config: &'a ArriClientConfig, + pub tests: TestClientTestsService<'a>, + pub adapters: TestClientAdaptersService<'a>, + pub users: TestClientUsersService<'a>, +} + +impl<'a> ArriClientService<'a> for TestClient<'a> { + fn create(config: &'a ArriClientConfig) -> Self { + Self { + config: &config, + tests: TestClientTestsService::create(config), + adapters: TestClientAdaptersService::create(config), + users: TestClientUsersService::create(config), + } + } +} + +impl TestClient<'_> {} + +pub struct TestClientTestsService<'a> { + config: &'a ArriClientConfig, +} + +impl<'a> ArriClientService<'a> for TestClientTestsService<'a> { + fn create(config: &'a ArriClientConfig) -> Self { + Self { config: &config } + } +} + +impl TestClientTestsService<'_> { + pub async fn empty_params_get_request(self: &Self) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!( + "{}/rpcs/tests/empty-params-get-request", + &self.config.base_url + ), + method: reqwest::Method::GET, + headers: self.config.headers, + client_version: "10".to_string(), + }, + None::, + |body| return DefaultPayload::from_json_string(body), + ) + .await + } + pub async fn empty_params_post_request(self: &Self) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!( + "{}/rpcs/tests/empty-params-post-request", + &self.config.base_url + ), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + None::, + |body| return DefaultPayload::from_json_string(body), + ) + .await + } + pub async fn empty_response_get_request( + self: &Self, + params: DefaultPayload, + ) -> Result<(), ArriServerError> { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!( + "{}/rpcs/tests/empty-response-get-request", + &self.config.base_url + ), + method: reqwest::Method::GET, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| {}, + ) + .await + } + pub async fn empty_response_post_request( + self: &Self, + params: DefaultPayload, + ) -> Result<(), ArriServerError> { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!( + "{}/rpcs/tests/empty-response-post-request", + &self.config.base_url + ), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| {}, + ) + .await + } + /// If the target language supports it. Generated code should mark this procedure as deprecated. + #[deprecated] + pub async fn deprecated_rpc( + self: &Self, + params: DeprecatedRpcParams, + ) -> Result<(), ArriServerError> { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/rpcs/tests/deprecated-rpc", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| {}, + ) + .await + } + pub async fn send_error(self: &Self, params: SendErrorParams) -> Result<(), ArriServerError> { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/rpcs/tests/send-error", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| {}, + ) + .await + } + pub async fn send_object( + self: &Self, + params: ObjectWithEveryType, + ) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/rpcs/tests/send-object", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| return ObjectWithEveryType::from_json_string(body), + ) + .await + } + pub async fn send_object_with_nullable_fields( + self: &Self, + params: ObjectWithEveryNullableType, + ) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!( + "{}/rpcs/tests/send-object-with-nullable-fields", + &self.config.base_url + ), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| return ObjectWithEveryNullableType::from_json_string(body), + ) + .await + } + pub async fn send_partial_object( + self: &Self, + params: ObjectWithEveryOptionalType, + ) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/rpcs/tests/send-partial-object", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| return ObjectWithEveryOptionalType::from_json_string(body), + ) + .await + } + pub async fn send_recursive_object( + self: &Self, + params: RecursiveObject, + ) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/rpcs/tests/send-recursive-object", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| return RecursiveObject::from_json_string(body), + ) + .await + } + pub async fn send_recursive_union( + self: &Self, + params: RecursiveUnion, + ) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/rpcs/tests/send-recursive-union", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| return RecursiveUnion::from_json_string(body), + ) + .await + } +} + +pub struct TestClientAdaptersService<'a> { + config: &'a ArriClientConfig, +} + +impl<'a> ArriClientService<'a> for TestClientAdaptersService<'a> { + fn create(config: &'a ArriClientConfig) -> Self { + Self { config: &config } + } +} + +impl TestClientAdaptersService<'_> { + pub async fn typebox( + self: &Self, + params: TypeBoxObject, + ) -> Result { + parsed_arri_request( + ArriParsedRequestOptions { + http_client: &self.config.http_client, + url: format!("{}/rpcs/adapters/typebox", &self.config.base_url), + method: reqwest::Method::POST, + headers: self.config.headers, + client_version: "10".to_string(), + }, + Some(params), + |body| return TypeBoxObject::from_json_string(body), + ) + .await + } +} + +pub struct TestClientUsersService<'a> { + config: &'a ArriClientConfig, +} + +impl<'a> ArriClientService<'a> for TestClientUsersService<'a> { + fn create(config: &'a ArriClientConfig) -> Self { + Self { config: &config } + } +} + +impl TestClientUsersService<'_> {} + +#[derive(Clone, Debug, PartialEq)] +pub struct ManuallyAddedModel { + pub hello: String, +} + +impl ArriModel for ManuallyAddedModel { + fn new() -> Self { + Self { + hello: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let hello = match _val_.get("hello") { + Some(serde_json::Value::String(hello_val)) => hello_val.to_owned(), + _ => "".to_string(), + }; + Self { hello } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"hello\":"); + _json_output_.push_str(serialize_string(&self.hello).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("hello={}", &self.hello)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct DefaultPayload { + pub message: String, +} + +impl ArriModel for DefaultPayload { + fn new() -> Self { + Self { + message: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let message = match _val_.get("message") { + Some(serde_json::Value::String(message_val)) => message_val.to_owned(), + _ => "".to_string(), + }; + Self { message } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"message\":"); + _json_output_.push_str(serialize_string(&self.message).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("message={}", &self.message)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct TypeBoxObject { + pub string: String, + pub boolean: bool, + pub integer: i32, + pub number: f64, + pub enum_field: TypeBoxObjectEnumField, + pub object: TypeBoxObjectObject, + pub array: Vec, + pub optional_string: Option, +} + +impl ArriModel for TypeBoxObject { + fn new() -> Self { + Self { + string: "".to_string(), + boolean: false, + integer: 0, + number: 0.0, + enum_field: TypeBoxObjectEnumField::default(), + object: TypeBoxObjectObject::new(), + array: Vec::new(), + optional_string: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => string_val.to_owned(), + _ => "".to_string(), + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => boolean_val.to_owned(), + _ => false, + }; + let integer = match _val_.get("integer") { + Some(serde_json::Value::Number(integer_val)) => { + i32::try_from(integer_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let number = match _val_.get("number") { + Some(serde_json::Value::Number(number_val)) => { + number_val.as_f64().unwrap_or(0.0) + } + _ => 0.0, + }; + let enum_field = match _val_.get("enumField") { + Some(serde_json::Value::String(enum_field_val)) => { + TypeBoxObjectEnumField::from_string(enum_field_val.to_owned()) + } + _ => TypeBoxObjectEnumField::default(), + }; + let object = match _val_.get("object") { + Some(object_val) => TypeBoxObjectObject::from_json(object_val.to_owned()), + _ => TypeBoxObjectObject::new(), + }; + let array = match _val_.get("array") { + Some(serde_json::Value::Array(array_val)) => { + let mut array_val_result: Vec = Vec::new(); + for array_val_element in array_val { + array_val_result.push(match Some(array_val_element) { + Some(serde_json::Value::Bool(array_val_element_val)) => { + array_val_element_val.to_owned() + } + _ => false, + }); + } + array_val_result + } + _ => Vec::new(), + }; + let optional_string = match _val_.get("optionalString") { + Some(serde_json::Value::String(optional_string_val)) => { + Some(optional_string_val.to_owned()) + } + _ => None, + }; + Self { + string, + boolean, + integer, + number, + enum_field, + object, + array, + optional_string, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"string\":"); + _json_output_.push_str(serialize_string(&self.string).as_str()); + _json_output_.push_str(",\"boolean\":"); + _json_output_.push_str(&self.boolean.to_string().as_str()); + _json_output_.push_str(",\"integer\":"); + _json_output_.push_str(&self.integer.to_string().as_str()); + _json_output_.push_str(",\"number\":"); + _json_output_.push_str(&self.number.to_string().as_str()); + _json_output_.push_str(",\"enumField\":"); + _json_output_.push_str(format!("\"{}\"", &self.enum_field.serial_value()).as_str()); + _json_output_.push_str(",\"object\":"); + _json_output_.push_str(&self.object.to_json_string().as_str()); + _json_output_.push_str(",\"array\":"); + _json_output_.push('['); + for (_index_, _element_) in self.array.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_string().as_str()); + } + _json_output_.push(']'); + match &self.optional_string { + Some(optional_string_val) => { + _json_output_.push_str(",\"optionalString\":"); + _json_output_.push_str(serialize_string(optional_string_val).as_str()) + } + _ => {} + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("string={}", &self.string)); + _query_parts_.push(format!("boolean={}", &self.boolean)); + _query_parts_.push(format!("integer={}", &self.integer)); + _query_parts_.push(format!("number={}", &self.number)); + _query_parts_.push(format!("enumField={}", &self.enum_field.serial_value())); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /TypeBoxObject/object."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /TypeBoxObject/array."); + match &self.optional_string { + Some(optional_string_val) => { + _query_parts_.push(format!("optionalString={}", optional_string_val)); + } + _ => {} + }; + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum TypeBoxObjectEnumField { + A, + B, + C, +} + +impl ArriEnum for TypeBoxObjectEnumField { + fn default() -> Self { + TypeBoxObjectEnumField::A + } + fn from_string(input: String) -> Self { + match input.as_str() { + "A" => Self::A, + "B" => Self::B, + "C" => Self::C, + _ => Self::default(), + } + } + fn serial_value(&self) -> String { + match &self { + TypeBoxObjectEnumField::A => "A".to_string(), + TypeBoxObjectEnumField::B => "B".to_string(), + TypeBoxObjectEnumField::C => "C".to_string(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct TypeBoxObjectObject { + pub string: String, +} + +impl ArriModel for TypeBoxObjectObject { + fn new() -> Self { + Self { + string: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => string_val.to_owned(), + _ => "".to_string(), + }; + Self { string } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"string\":"); + _json_output_.push_str(serialize_string(&self.string).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("string={}", &self.string)); + _query_parts_.join("&") + } +} + +#[deprecated] +#[derive(Clone, Debug, PartialEq)] +pub struct DeprecatedRpcParams { + #[deprecated] + pub deprecated_field: String, +} + +impl ArriModel for DeprecatedRpcParams { + fn new() -> Self { + Self { + deprecated_field: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let deprecated_field = match _val_.get("deprecatedField") { + Some(serde_json::Value::String(deprecated_field_val)) => { + deprecated_field_val.to_owned() + } + _ => "".to_string(), + }; + Self { deprecated_field } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"deprecatedField\":"); + _json_output_.push_str(serialize_string(&self.deprecated_field).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("deprecatedField={}", &self.deprecated_field)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct SendErrorParams { + pub code: u16, + pub message: String, +} + +impl ArriModel for SendErrorParams { + fn new() -> Self { + Self { + code: 0, + message: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let code = match _val_.get("code") { + Some(serde_json::Value::Number(code_val)) => { + u16::try_from(code_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let message = match _val_.get("message") { + Some(serde_json::Value::String(message_val)) => message_val.to_owned(), + _ => "".to_string(), + }; + Self { code, message } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"code\":"); + _json_output_.push_str(&self.code.to_string().as_str()); + _json_output_.push_str(",\"message\":"); + _json_output_.push_str(serialize_string(&self.message).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("code={}", &self.code)); + _query_parts_.push(format!("message={}", &self.message)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryType { + pub any: serde_json::Value, + pub boolean: bool, + pub string: String, + pub timestamp: DateTime, + pub float32: f32, + pub float64: f64, + pub int8: i8, + pub uint8: u8, + pub int16: i16, + pub uint16: u16, + pub int32: i32, + pub uint32: u32, + pub int64: i64, + pub uint64: u64, + pub enumerator: ObjectWithEveryTypeEnumerator, + pub array: Vec, + pub object: ObjectWithEveryTypeObject, + pub record: BTreeMap, + pub discriminator: ObjectWithEveryTypeDiscriminator, + pub nested_object: ObjectWithEveryTypeNestedObject, + pub nested_array: Vec>, +} + +impl ArriModel for ObjectWithEveryType { + fn new() -> Self { + Self { + any: serde_json::Value::Null, + boolean: false, + string: "".to_string(), + timestamp: DateTime::default(), + float32: 0.0, + float64: 0.0, + int8: 0, + uint8: 0, + int16: 0, + uint16: 0, + int32: 0, + uint32: 0, + int64: 0, + uint64: 0, + enumerator: ObjectWithEveryTypeEnumerator::default(), + array: Vec::new(), + object: ObjectWithEveryTypeObject::new(), + record: BTreeMap::new(), + discriminator: ObjectWithEveryTypeDiscriminator::new(), + nested_object: ObjectWithEveryTypeNestedObject::new(), + nested_array: Vec::new(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let any = match _val_.get("any") { + Some(any_val) => any_val.to_owned(), + _ => serde_json::Value::Null, + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => boolean_val.to_owned(), + _ => false, + }; + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => string_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let float32 = match _val_.get("float32") { + Some(serde_json::Value::Number(float32_val)) => { + float32_val.as_f64().unwrap_or(0.0) as f32 + } + _ => 0.0, + }; + let float64 = match _val_.get("float64") { + Some(serde_json::Value::Number(float64_val)) => { + float64_val.as_f64().unwrap_or(0.0) + } + _ => 0.0, + }; + let int8 = match _val_.get("int8") { + Some(serde_json::Value::Number(int8_val)) => { + i8::try_from(int8_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let uint8 = match _val_.get("uint8") { + Some(serde_json::Value::Number(uint8_val)) => { + u8::try_from(uint8_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let int16 = match _val_.get("int16") { + Some(serde_json::Value::Number(int16_val)) => { + i16::try_from(int16_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let uint16 = match _val_.get("uint16") { + Some(serde_json::Value::Number(uint16_val)) => { + u16::try_from(uint16_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let int32 = match _val_.get("int32") { + Some(serde_json::Value::Number(int32_val)) => { + i32::try_from(int32_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let uint32 = match _val_.get("uint32") { + Some(serde_json::Value::Number(uint32_val)) => { + u32::try_from(uint32_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let int64 = match _val_.get("int64") { + Some(serde_json::Value::String(int64_val)) => { + int64_val.parse::().unwrap_or(0) + } + _ => 0, + }; + let uint64 = match _val_.get("uint64") { + Some(serde_json::Value::String(uint64_val)) => { + uint64_val.parse::().unwrap_or(0) + } + _ => 0, + }; + let enumerator = match _val_.get("enumerator") { + Some(serde_json::Value::String(enumerator_val)) => { + ObjectWithEveryTypeEnumerator::from_string(enumerator_val.to_owned()) + } + _ => ObjectWithEveryTypeEnumerator::default(), + }; + let array = match _val_.get("array") { + Some(serde_json::Value::Array(array_val)) => { + let mut array_val_result: Vec = Vec::new(); + for array_val_element in array_val { + array_val_result.push(match Some(array_val_element) { + Some(serde_json::Value::Bool(array_val_element_val)) => { + array_val_element_val.to_owned() + } + _ => false, + }); + } + array_val_result + } + _ => Vec::new(), + }; + let object = match _val_.get("object") { + Some(object_val) => ObjectWithEveryTypeObject::from_json(object_val.to_owned()), + _ => ObjectWithEveryTypeObject::new(), + }; + let record = match _val_.get("record") { + Some(serde_json::Value::Object(record_val)) => { + let mut record_val_result: BTreeMap = BTreeMap::new(); + for (_key_, _value_) in record_val.into_iter() { + record_val_result.insert( + _key_.to_owned(), + match Some(_value_.to_owned()) { + Some(serde_json::Value::Bool(value_val)) => { + value_val.to_owned() + } + _ => false, + }, + ); + } + record_val_result + } + _ => BTreeMap::new(), + }; + let discriminator = match _val_.get("discriminator") { + Some(discriminator_val) => match discriminator_val { + serde_json::Value::Object(_) => { + ObjectWithEveryTypeDiscriminator::from_json( + discriminator_val.to_owned(), + ) + } + _ => ObjectWithEveryTypeDiscriminator::new(), + }, + _ => ObjectWithEveryTypeDiscriminator::new(), + }; + let nested_object = match _val_.get("nestedObject") { + Some(nested_object_val) => { + ObjectWithEveryTypeNestedObject::from_json(nested_object_val.to_owned()) + } + _ => ObjectWithEveryTypeNestedObject::new(), + }; + let nested_array = match _val_.get("nestedArray") { + Some(serde_json::Value::Array(nested_array_val)) => { + let mut nested_array_val_result: Vec< + Vec, + > = Vec::new(); + for nested_array_val_element in nested_array_val { + nested_array_val_result.push(match Some(nested_array_val_element) { + Some(serde_json::Value::Array(nested_array_val_element_val)) => { + let mut nested_array_val_element_val_result: Vec = Vec::new(); + for nested_array_val_element_val_element in nested_array_val_element_val { + nested_array_val_element_val_result.push(match Some(nested_array_val_element_val_element) { + Some(nested_array_val_element_val_element_val) => ObjectWithEveryTypeNestedArrayElementElement::from_json(nested_array_val_element_val_element_val.to_owned()), + _ => ObjectWithEveryTypeNestedArrayElementElement::new(), + }); + } + nested_array_val_element_val_result + } + _ => Vec::new(), + }); + } + nested_array_val_result + } + _ => Vec::new(), + }; + Self { + any, + boolean, + string, + timestamp, + float32, + float64, + int8, + uint8, + int16, + uint16, + int32, + uint32, + int64, + uint64, + enumerator, + array, + object, + record, + discriminator, + nested_object, + nested_array, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"any\":"); + _json_output_.push_str( + serde_json::to_string(&self.any) + .unwrap_or("null".to_string()) + .as_str(), + ); + _json_output_.push_str(",\"boolean\":"); + _json_output_.push_str(&self.boolean.to_string().as_str()); + _json_output_.push_str(",\"string\":"); + _json_output_.push_str(serialize_string(&self.string).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push_str(",\"float32\":"); + _json_output_.push_str(&self.float32.to_string().as_str()); + _json_output_.push_str(",\"float64\":"); + _json_output_.push_str(&self.float64.to_string().as_str()); + _json_output_.push_str(",\"int8\":"); + _json_output_.push_str(&self.int8.to_string().as_str()); + _json_output_.push_str(",\"uint8\":"); + _json_output_.push_str(&self.uint8.to_string().as_str()); + _json_output_.push_str(",\"int16\":"); + _json_output_.push_str(&self.int16.to_string().as_str()); + _json_output_.push_str(",\"uint16\":"); + _json_output_.push_str(&self.uint16.to_string().as_str()); + _json_output_.push_str(",\"int32\":"); + _json_output_.push_str(&self.int32.to_string().as_str()); + _json_output_.push_str(",\"uint32\":"); + _json_output_.push_str(&self.uint32.to_string().as_str()); + _json_output_.push_str(",\"int64\":"); + _json_output_.push_str(format!("\"{}\"", &self.int64).as_str()); + _json_output_.push_str(",\"uint64\":"); + _json_output_.push_str(format!("\"{}\"", &self.uint64).as_str()); + _json_output_.push_str(",\"enumerator\":"); + _json_output_.push_str(format!("\"{}\"", &self.enumerator.serial_value()).as_str()); + _json_output_.push_str(",\"array\":"); + _json_output_.push('['); + for (_index_, _element_) in self.array.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_string().as_str()); + } + _json_output_.push(']'); + _json_output_.push_str(",\"object\":"); + _json_output_.push_str(&self.object.to_json_string().as_str()); + _json_output_.push_str(",\"record\":"); + _json_output_.push('{'); + for (_index_, (_key_, _value_)) in self.record.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(format!("\"{}\":", _key_).as_str()); + _json_output_.push_str(_value_.to_string().as_str()); + } + _json_output_.push('}'); + _json_output_.push_str(",\"discriminator\":"); + _json_output_.push_str(&self.discriminator.to_json_string().as_str()); + _json_output_.push_str(",\"nestedObject\":"); + _json_output_.push_str(&self.nested_object.to_json_string().as_str()); + _json_output_.push_str(",\"nestedArray\":"); + _json_output_.push('['); + for (_index_, _element_) in self.nested_array.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push('['); + for (_index_, _element_) in _element_.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_json_string().as_str()); + } + _json_output_.push(']'); + } + _json_output_.push(']'); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + println!("[WARNING] cannot serialize any's to query params. Skipping field at /ObjectWithEveryType/any."); + _query_parts_.push(format!("boolean={}", &self.boolean)); + _query_parts_.push(format!("string={}", &self.string)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + _query_parts_.push(format!("float32={}", &self.float32)); + _query_parts_.push(format!("float64={}", &self.float64)); + _query_parts_.push(format!("int8={}", &self.int8)); + _query_parts_.push(format!("uint8={}", &self.uint8)); + _query_parts_.push(format!("int16={}", &self.int16)); + _query_parts_.push(format!("uint16={}", &self.uint16)); + _query_parts_.push(format!("int32={}", &self.int32)); + _query_parts_.push(format!("uint32={}", &self.uint32)); + _query_parts_.push(format!("int64={}", &self.int64)); + _query_parts_.push(format!("uint64={}", &self.uint64)); + _query_parts_.push(format!("enumerator={}", &self.enumerator.serial_value())); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithEveryType/array."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryType/object."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryType/record."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryType/discriminator."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryType/nestedObject."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithEveryType/nestedArray."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ObjectWithEveryTypeEnumerator { + A, + B, + C, +} + +impl ArriEnum for ObjectWithEveryTypeEnumerator { + fn default() -> Self { + ObjectWithEveryTypeEnumerator::A + } + fn from_string(input: String) -> Self { + match input.as_str() { + "A" => Self::A, + "B" => Self::B, + "C" => Self::C, + _ => Self::default(), + } + } + fn serial_value(&self) -> String { + match &self { + ObjectWithEveryTypeEnumerator::A => "A".to_string(), + ObjectWithEveryTypeEnumerator::B => "B".to_string(), + ObjectWithEveryTypeEnumerator::C => "C".to_string(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryTypeObject { + pub string: String, + pub boolean: bool, + pub timestamp: DateTime, +} + +impl ArriModel for ObjectWithEveryTypeObject { + fn new() -> Self { + Self { + string: "".to_string(), + boolean: false, + timestamp: DateTime::default(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => string_val.to_owned(), + _ => "".to_string(), + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => boolean_val.to_owned(), + _ => false, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + Self { + string, + boolean, + timestamp, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"string\":"); + _json_output_.push_str(serialize_string(&self.string).as_str()); + _json_output_.push_str(",\"boolean\":"); + _json_output_.push_str(&self.boolean.to_string().as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("string={}", &self.string)); + _query_parts_.push(format!("boolean={}", &self.boolean)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ObjectWithEveryTypeDiscriminator { + A { title: String }, + B { title: String, description: String }, +} + +impl ArriModel for ObjectWithEveryTypeDiscriminator { + fn new() -> Self { + Self::A { + title: "".to_string(), + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let r#type = match _val_.get("type") { + Some(serde_json::Value::String(r#type_val)) => r#type_val.to_owned(), + _ => "".to_string(), + }; + match r#type.as_str() { + "A" => { + let title = match _val_.get("title") { + Some(serde_json::Value::String(title_val)) => title_val.to_owned(), + _ => "".to_string(), + }; + Self::A { title } + } + "B" => { + let title = match _val_.get("title") { + Some(serde_json::Value::String(title_val)) => title_val.to_owned(), + _ => "".to_string(), + }; + let description = match _val_.get("description") { + Some(serde_json::Value::String(description_val)) => { + description_val.to_owned() + } + _ => "".to_string(), + }; + Self::B { title, description } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::A { title } => { + _json_output_.push_str("\"type\":\"A\""); + _json_output_.push_str(",\"title\":"); + _json_output_.push_str(serialize_string(title).as_str()); + } + Self::B { title, description } => { + _json_output_.push_str("\"type\":\"B\""); + _json_output_.push_str(",\"title\":"); + _json_output_.push_str(serialize_string(title).as_str()); + _json_output_.push_str(",\"description\":"); + _json_output_.push_str(serialize_string(description).as_str()); + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::A { title } => { + _query_parts_.push(format!("type=A")); + _query_parts_.push(format!("title={}", title)); + } + Self::B { title, description } => { + _query_parts_.push(format!("type=B")); + _query_parts_.push(format!("title={}", title)); + _query_parts_.push(format!("description={}", description)); + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryTypeNestedObject { + pub id: String, + pub timestamp: DateTime, + pub data: ObjectWithEveryTypeNestedObjectData, +} + +impl ArriModel for ObjectWithEveryTypeNestedObject { + fn new() -> Self { + Self { + id: "".to_string(), + timestamp: DateTime::default(), + data: ObjectWithEveryTypeNestedObjectData::new(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let data = match _val_.get("data") { + Some(data_val) => { + ObjectWithEveryTypeNestedObjectData::from_json(data_val.to_owned()) + } + _ => ObjectWithEveryTypeNestedObjectData::new(), + }; + Self { + id, + timestamp, + data, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push_str(",\"data\":"); + _json_output_.push_str(&self.data.to_json_string().as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryTypeNestedObject/data."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryTypeNestedObjectData { + pub id: String, + pub timestamp: DateTime, + pub data: ObjectWithEveryTypeNestedObjectDataData, +} + +impl ArriModel for ObjectWithEveryTypeNestedObjectData { + fn new() -> Self { + Self { + id: "".to_string(), + timestamp: DateTime::default(), + data: ObjectWithEveryTypeNestedObjectDataData::new(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let data = match _val_.get("data") { + Some(data_val) => { + ObjectWithEveryTypeNestedObjectDataData::from_json(data_val.to_owned()) + } + _ => ObjectWithEveryTypeNestedObjectDataData::new(), + }; + Self { + id, + timestamp, + data, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push_str(",\"data\":"); + _json_output_.push_str(&self.data.to_json_string().as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryTypeNestedObjectData/data."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryTypeNestedObjectDataData { + pub id: String, + pub timestamp: DateTime, +} + +impl ArriModel for ObjectWithEveryTypeNestedObjectDataData { + fn new() -> Self { + Self { + id: "".to_string(), + timestamp: DateTime::default(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + Self { id, timestamp } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryTypeNestedArrayElementElement { + pub id: String, + pub timestamp: DateTime, +} + +impl ArriModel for ObjectWithEveryTypeNestedArrayElementElement { + fn new() -> Self { + Self { + id: "".to_string(), + timestamp: DateTime::default(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + Self { id, timestamp } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryNullableType { + pub any: serde_json::Value, + pub boolean: Option, + pub string: Option, + pub timestamp: Option>, + pub float32: Option, + pub float64: Option, + pub int8: Option, + pub uint8: Option, + pub int16: Option, + pub uint16: Option, + pub int32: Option, + pub uint32: Option, + pub int64: Option, + pub uint64: Option, + pub enumerator: Option, + pub array: Option>>, + pub object: Option, + pub record: Option>>, + pub discriminator: Option, + pub nested_object: Option, + pub nested_array: + Option>>>>, +} + +impl ArriModel for ObjectWithEveryNullableType { + fn new() -> Self { + Self { + any: serde_json::Value::Null, + boolean: None, + string: None, + timestamp: None, + float32: None, + float64: None, + int8: None, + uint8: None, + int16: None, + uint16: None, + int32: None, + uint32: None, + int64: None, + uint64: None, + enumerator: None, + array: None, + object: None, + record: None, + discriminator: None, + nested_object: None, + nested_array: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let any = match _val_.get("any") { + Some(any_val) => any_val.to_owned(), + _ => serde_json::Value::Null, + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => Some(boolean_val.to_owned()), + _ => None, + }; + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => Some(string_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + let float32 = match _val_.get("float32") { + Some(serde_json::Value::Number(float32_val)) => match float32_val.as_f64() { + Some(float32_val_result) => Some(float32_val_result as f32), + _ => None, + }, + _ => None, + }; + let float64 = match _val_.get("float64") { + Some(serde_json::Value::Number(float64_val)) => match float64_val.as_f64() { + Some(float64_val_result) => Some(float64_val_result), + _ => None, + }, + _ => None, + }; + let int8 = match _val_.get("int8") { + Some(serde_json::Value::Number(int8_val)) => match int8_val.as_i64() { + Some(int8_val_result) => match i8::try_from(int8_val_result) { + Ok(int8_val_result_val) => Some(int8_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint8 = match _val_.get("uint8") { + Some(serde_json::Value::Number(uint8_val)) => match uint8_val.as_u64() { + Some(uint8_val_result) => match u8::try_from(uint8_val_result) { + Ok(uint8_val_result_val) => Some(uint8_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int16 = match _val_.get("int16") { + Some(serde_json::Value::Number(int16_val)) => match int16_val.as_i64() { + Some(int16_val_result) => match i16::try_from(int16_val_result) { + Ok(int16_val_result_val) => Some(int16_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint16 = match _val_.get("uint16") { + Some(serde_json::Value::Number(uint16_val)) => match uint16_val.as_u64() { + Some(uint16_val_result) => match u16::try_from(uint16_val_result) { + Ok(uint16_val_result_val) => Some(uint16_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int32 = match _val_.get("int32") { + Some(serde_json::Value::Number(int32_val)) => match int32_val.as_i64() { + Some(int32_val_result) => match i32::try_from(int32_val_result) { + Ok(int32_val_result_val) => Some(int32_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint32 = match _val_.get("uint32") { + Some(serde_json::Value::Number(uint32_val)) => match uint32_val.as_u64() { + Some(uint32_val_result) => match u32::try_from(uint32_val_result) { + Ok(uint32_val_result_val) => Some(uint32_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int64 = match _val_.get("int64") { + Some(serde_json::Value::String(int64_val)) => match int64_val.parse::() { + Ok(int64_val_result) => Some(int64_val_result), + Err(_) => None, + }, + _ => None, + }; + let uint64 = match _val_.get("uint64") { + Some(serde_json::Value::String(uint64_val)) => { + match uint64_val.parse::() { + Ok(uint64_val_result) => Some(uint64_val_result), + Err(_) => None, + } + } + _ => None, + }; + let enumerator = match _val_.get("enumerator") { + Some(serde_json::Value::String(enumerator_val)) => { + Some(ObjectWithEveryNullableTypeEnumerator::from_string( + enumerator_val.to_owned(), + )) + } + _ => None, + }; + let array = match _val_.get("array") { + Some(serde_json::Value::Array(array_val)) => { + let mut array_val_result: Vec> = Vec::new(); + for array_val_element in array_val { + array_val_result.push(match Some(array_val_element) { + Some(serde_json::Value::Bool(array_val_element_val)) => { + Some(array_val_element_val.to_owned()) + } + _ => None, + }); + } + Some(array_val_result) + } + _ => None, + }; + let object = match _val_.get("object") { + Some(object_val) => match object_val { + serde_json::Value::Object(_) => Some( + ObjectWithEveryNullableTypeObject::from_json(object_val.to_owned()), + ), + _ => None, + }, + _ => None, + }; + let record = match _val_.get("record") { + Some(serde_json::Value::Object(record_val)) => { + let mut record_val_result: BTreeMap> = BTreeMap::new(); + for (_key_, _value_) in record_val.into_iter() { + record_val_result.insert( + _key_.to_owned(), + match Some(_value_.to_owned()) { + Some(serde_json::Value::Bool(value_val)) => { + Some(value_val.to_owned()) + } + _ => None, + }, + ); + } + Some(record_val_result) + } + _ => None, + }; + let discriminator = match _val_.get("discriminator") { + Some(discriminator_val) => match discriminator_val { + serde_json::Value::Object(_) => { + Some(ObjectWithEveryNullableTypeDiscriminator::from_json( + discriminator_val.to_owned(), + )) + } + _ => None, + }, + _ => None, + }; + let nested_object = match _val_.get("nestedObject") { + Some(nested_object_val) => match nested_object_val { + serde_json::Value::Object(_) => { + Some(ObjectWithEveryNullableTypeNestedObject::from_json( + nested_object_val.to_owned(), + )) + } + _ => None, + }, + _ => None, + }; + let nested_array = match _val_.get("nestedArray") { + Some(serde_json::Value::Array(nested_array_val)) => { + let mut nested_array_val_result: Vec< + Option< + Vec>, + >, + > = Vec::new(); + for nested_array_val_element in nested_array_val { + nested_array_val_result.push(match Some(nested_array_val_element) { + Some(serde_json::Value::Array(nested_array_val_element_val)) => { + let mut nested_array_val_element_val_result: Vec> = Vec::new(); + for nested_array_val_element_val_element in nested_array_val_element_val { + nested_array_val_element_val_result.push(match Some(nested_array_val_element_val_element) { + Some(nested_array_val_element_val_element_val) => match nested_array_val_element_val_element_val { + serde_json::Value::Object(_) => { + Some(ObjectWithEveryNullableTypeNestedArrayElementElement::from_json(nested_array_val_element_val_element_val.to_owned())) + } + _ => None, + }, + _ => None, + }); + } + Some(nested_array_val_element_val_result) + } + _ => None, + }); + } + Some(nested_array_val_result) + } + _ => None, + }; + Self { + any, + boolean, + string, + timestamp, + float32, + float64, + int8, + uint8, + int16, + uint16, + int32, + uint32, + int64, + uint64, + enumerator, + array, + object, + record, + discriminator, + nested_object, + nested_array, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"any\":"); + _json_output_.push_str( + serde_json::to_string(&self.any) + .unwrap_or("null".to_string()) + .as_str(), + ); + _json_output_.push_str(",\"boolean\":"); + match &self.boolean { + Some(boolean_val) => { + _json_output_.push_str(boolean_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"string\":"); + match &self.string { + Some(string_val) => { + _json_output_.push_str(serialize_string(string_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"timestamp\":"); + match &self.timestamp { + Some(timestamp_val) => { + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"float32\":"); + match &self.float32 { + Some(float32_val) => { + _json_output_.push_str(float32_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"float64\":"); + match &self.float64 { + Some(float64_val) => { + _json_output_.push_str(float64_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"int8\":"); + match &self.int8 { + Some(int8_val) => { + _json_output_.push_str(int8_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"uint8\":"); + match &self.uint8 { + Some(uint8_val) => { + _json_output_.push_str(uint8_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"int16\":"); + match &self.int16 { + Some(int16_val) => { + _json_output_.push_str(int16_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"uint16\":"); + match &self.uint16 { + Some(uint16_val) => { + _json_output_.push_str(uint16_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"int32\":"); + match &self.int32 { + Some(int32_val) => { + _json_output_.push_str(int32_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"uint32\":"); + match &self.uint32 { + Some(uint32_val) => { + _json_output_.push_str(uint32_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"int64\":"); + match &self.int64 { + Some(int64_val) => { + _json_output_.push_str(format!("\"{}\"", int64_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"uint64\":"); + match &self.uint64 { + Some(uint64_val) => { + _json_output_.push_str(format!("\"{}\"", uint64_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"enumerator\":"); + match &self.enumerator { + Some(enumerator_val) => { + _json_output_.push_str(format!("\"{}\"", enumerator_val.serial_value()).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"array\":"); + match &self.array { + Some(array_val) => { + _json_output_.push('['); + for (_index_, _element_) in array_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + match _element_ { + Some(_element_val_) => { + _json_output_.push_str(_element_val_.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + } + _json_output_.push(']'); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"object\":"); + match &self.object { + Some(object_val) => { + _json_output_.push_str(object_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"record\":"); + match &self.record { + Some(record_val) => { + _json_output_.push('{'); + for (_index_, (_key_, _value_)) in record_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(format!("\"{}\":", _key_).as_str()); + match _value_ { + Some(value_val) => { + _json_output_.push_str(value_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + } + } + _json_output_.push('}'); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"discriminator\":"); + match &self.discriminator { + Some(discriminator_val) => { + _json_output_.push_str(discriminator_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"nestedObject\":"); + match &self.nested_object { + Some(nested_object_val) => { + _json_output_.push_str(nested_object_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"nestedArray\":"); + match &self.nested_array { + Some(nested_array_val) => { + _json_output_.push('['); + for (_index_, _element_) in nested_array_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + match _element_ { + Some(_element_val_) => { + _json_output_.push('['); + for (_index_, _element_) in _element_val_.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + match _element_ { + Some(_element_val_) => { + _json_output_ + .push_str(_element_val_.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + } + _json_output_.push(']'); + } + _ => { + _json_output_.push_str("null"); + } + }; + } + _json_output_.push(']'); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + println!("[WARNING] cannot serialize any's to query params. Skipping field at /ObjectWithEveryNullableType/any."); + match &self.boolean { + Some(boolean_val) => { + _query_parts_.push(format!("boolean={}", boolean_val)); + } + _ => { + _query_parts_.push("boolean=null".to_string()); + } + }; + match &self.string { + Some(string_val) => { + _query_parts_.push(format!("string={}", string_val)); + } + _ => { + _query_parts_.push("string=null".to_string()); + } + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => { + _query_parts_.push("timestamp=null".to_string()); + } + }; + match &self.float32 { + Some(float32_val) => { + _query_parts_.push(format!("float32={}", float32_val)); + } + _ => { + _query_parts_.push("float32=null".to_string()); + } + }; + match &self.float64 { + Some(float64_val) => { + _query_parts_.push(format!("float64={}", float64_val)); + } + _ => { + _query_parts_.push("float64=null".to_string()); + } + }; + match &self.int8 { + Some(int8_val) => { + _query_parts_.push(format!("int8={}", int8_val)); + } + _ => { + _query_parts_.push("int8=null".to_string()); + } + }; + match &self.uint8 { + Some(uint8_val) => { + _query_parts_.push(format!("uint8={}", uint8_val)); + } + _ => { + _query_parts_.push("uint8=null".to_string()); + } + }; + match &self.int16 { + Some(int16_val) => { + _query_parts_.push(format!("int16={}", int16_val)); + } + _ => { + _query_parts_.push("int16=null".to_string()); + } + }; + match &self.uint16 { + Some(uint16_val) => { + _query_parts_.push(format!("uint16={}", uint16_val)); + } + _ => { + _query_parts_.push("uint16=null".to_string()); + } + }; + match &self.int32 { + Some(int32_val) => { + _query_parts_.push(format!("int32={}", int32_val)); + } + _ => { + _query_parts_.push("int32=null".to_string()); + } + }; + match &self.uint32 { + Some(uint32_val) => { + _query_parts_.push(format!("uint32={}", uint32_val)); + } + _ => { + _query_parts_.push("uint32=null".to_string()); + } + }; + match &self.int64 { + Some(int64_val) => { + _query_parts_.push(format!("int64={}", int64_val)); + } + _ => { + _query_parts_.push("int64=null".to_string()); + } + }; + match &self.uint64 { + Some(uint64_val) => { + _query_parts_.push(format!("uint64={}", uint64_val)); + } + _ => { + _query_parts_.push("uint64=null".to_string()); + } + }; + match &self.enumerator { + Some(enumerator_val) => { + _query_parts_.push(format!("enumerator={}", enumerator_val.serial_value())); + } + _ => { + _query_parts_.push("enumerator=null".to_string()); + } + }; + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithEveryNullableType/array."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryNullableType/object."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryNullableType/record."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryNullableType/discriminator."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryNullableType/nestedObject."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithEveryNullableType/nestedArray."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ObjectWithEveryNullableTypeEnumerator { + A, + B, + C, +} + +impl ArriEnum for ObjectWithEveryNullableTypeEnumerator { + fn default() -> Self { + ObjectWithEveryNullableTypeEnumerator::A + } + fn from_string(input: String) -> Self { + match input.as_str() { + "A" => Self::A, + "B" => Self::B, + "C" => Self::C, + _ => Self::default(), + } + } + fn serial_value(&self) -> String { + match &self { + ObjectWithEveryNullableTypeEnumerator::A => "A".to_string(), + ObjectWithEveryNullableTypeEnumerator::B => "B".to_string(), + ObjectWithEveryNullableTypeEnumerator::C => "C".to_string(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryNullableTypeObject { + pub string: Option, + pub boolean: Option, + pub timestamp: Option>, +} + +impl ArriModel for ObjectWithEveryNullableTypeObject { + fn new() -> Self { + Self { + string: None, + boolean: None, + timestamp: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => Some(string_val.to_owned()), + _ => None, + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => Some(boolean_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + Self { + string, + boolean, + timestamp, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"string\":"); + match &self.string { + Some(string_val) => { + _json_output_.push_str(serialize_string(string_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"boolean\":"); + match &self.boolean { + Some(boolean_val) => { + _json_output_.push_str(boolean_val.to_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"timestamp\":"); + match &self.timestamp { + Some(timestamp_val) => { + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self.string { + Some(string_val) => { + _query_parts_.push(format!("string={}", string_val)); + } + _ => { + _query_parts_.push("string=null".to_string()); + } + }; + match &self.boolean { + Some(boolean_val) => { + _query_parts_.push(format!("boolean={}", boolean_val)); + } + _ => { + _query_parts_.push("boolean=null".to_string()); + } + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => { + _query_parts_.push("timestamp=null".to_string()); + } + }; + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ObjectWithEveryNullableTypeDiscriminator { + A { + title: Option, + }, + B { + title: Option, + description: Option, + }, +} + +impl ArriModel for ObjectWithEveryNullableTypeDiscriminator { + fn new() -> Self { + Self::A { title: None } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let r#type = match _val_.get("type") { + Some(serde_json::Value::String(r#type_val)) => r#type_val.to_owned(), + _ => "".to_string(), + }; + match r#type.as_str() { + "A" => { + let title = match _val_.get("title") { + Some(serde_json::Value::String(title_val)) => { + Some(title_val.to_owned()) + } + _ => None, + }; + Self::A { title } + } + "B" => { + let title = match _val_.get("title") { + Some(serde_json::Value::String(title_val)) => { + Some(title_val.to_owned()) + } + _ => None, + }; + let description = match _val_.get("description") { + Some(serde_json::Value::String(description_val)) => { + Some(description_val.to_owned()) + } + _ => None, + }; + Self::B { title, description } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::A { title } => { + _json_output_.push_str("\"type\":\"A\""); + _json_output_.push_str(",\"title\":"); + match title { + Some(title_val) => { + _json_output_.push_str(serialize_string(title_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + } + Self::B { title, description } => { + _json_output_.push_str("\"type\":\"B\""); + _json_output_.push_str(",\"title\":"); + match title { + Some(title_val) => { + _json_output_.push_str(serialize_string(title_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"description\":"); + match description { + Some(description_val) => { + _json_output_.push_str(serialize_string(description_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::A { title } => { + _query_parts_.push(format!("type=A")); + match title { + Some(title_val) => { + _query_parts_.push(format!("title={}", title_val)); + } + _ => { + _query_parts_.push("title=null".to_string()); + } + }; + } + Self::B { title, description } => { + _query_parts_.push(format!("type=B")); + match title { + Some(title_val) => { + _query_parts_.push(format!("title={}", title_val)); + } + _ => { + _query_parts_.push("title=null".to_string()); + } + }; + match description { + Some(description_val) => { + _query_parts_.push(format!("description={}", description_val)); + } + _ => { + _query_parts_.push("description=null".to_string()); + } + }; + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryNullableTypeNestedObject { + pub id: Option, + pub timestamp: Option>, + pub data: Option, +} + +impl ArriModel for ObjectWithEveryNullableTypeNestedObject { + fn new() -> Self { + Self { + id: None, + timestamp: None, + data: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => Some(id_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + let data = match _val_.get("data") { + Some(data_val) => match data_val { + serde_json::Value::Object(_) => { + Some(ObjectWithEveryNullableTypeNestedObjectData::from_json( + data_val.to_owned(), + )) + } + _ => None, + }, + _ => None, + }; + Self { + id, + timestamp, + data, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + match &self.id { + Some(id_val) => { + _json_output_.push_str(serialize_string(id_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"timestamp\":"); + match &self.timestamp { + Some(timestamp_val) => { + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"data\":"); + match &self.data { + Some(data_val) => { + _json_output_.push_str(data_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self.id { + Some(id_val) => { + _query_parts_.push(format!("id={}", id_val)); + } + _ => { + _query_parts_.push("id=null".to_string()); + } + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => { + _query_parts_.push("timestamp=null".to_string()); + } + }; + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryNullableTypeNestedObject/data."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryNullableTypeNestedObjectData { + pub id: Option, + pub timestamp: Option>, + pub data: Option, +} + +impl ArriModel for ObjectWithEveryNullableTypeNestedObjectData { + fn new() -> Self { + Self { + id: None, + timestamp: None, + data: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => Some(id_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + let data = match _val_.get("data") { + Some(data_val) => match data_val { + serde_json::Value::Object(_) => { + Some(ObjectWithEveryNullableTypeNestedObjectDataData::from_json( + data_val.to_owned(), + )) + } + _ => None, + }, + _ => None, + }; + Self { + id, + timestamp, + data, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + match &self.id { + Some(id_val) => { + _json_output_.push_str(serialize_string(id_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"timestamp\":"); + match &self.timestamp { + Some(timestamp_val) => { + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"data\":"); + match &self.data { + Some(data_val) => { + _json_output_.push_str(data_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self.id { + Some(id_val) => { + _query_parts_.push(format!("id={}", id_val)); + } + _ => { + _query_parts_.push("id=null".to_string()); + } + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => { + _query_parts_.push("timestamp=null".to_string()); + } + }; + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryNullableTypeNestedObjectData/data."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryNullableTypeNestedObjectDataData { + pub id: Option, + pub timestamp: Option>, +} + +impl ArriModel for ObjectWithEveryNullableTypeNestedObjectDataData { + fn new() -> Self { + Self { + id: None, + timestamp: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => Some(id_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + Self { id, timestamp } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + match &self.id { + Some(id_val) => { + _json_output_.push_str(serialize_string(id_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"timestamp\":"); + match &self.timestamp { + Some(timestamp_val) => { + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self.id { + Some(id_val) => { + _query_parts_.push(format!("id={}", id_val)); + } + _ => { + _query_parts_.push("id=null".to_string()); + } + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => { + _query_parts_.push("timestamp=null".to_string()); + } + }; + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryNullableTypeNestedArrayElementElement { + pub id: Option, + pub timestamp: Option>, +} + +impl ArriModel for ObjectWithEveryNullableTypeNestedArrayElementElement { + fn new() -> Self { + Self { + id: None, + timestamp: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => Some(id_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + Self { id, timestamp } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + match &self.id { + Some(id_val) => { + _json_output_.push_str(serialize_string(id_val).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"timestamp\":"); + match &self.timestamp { + Some(timestamp_val) => { + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self.id { + Some(id_val) => { + _query_parts_.push(format!("id={}", id_val)); + } + _ => { + _query_parts_.push("id=null".to_string()); + } + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => { + _query_parts_.push("timestamp=null".to_string()); + } + }; + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryOptionalType { + pub any: Option, + pub boolean: Option, + pub string: Option, + pub timestamp: Option>, + pub float32: Option, + pub float64: Option, + pub int8: Option, + pub uint8: Option, + pub int16: Option, + pub uint16: Option, + pub int32: Option, + pub uint32: Option, + pub int64: Option, + pub uint64: Option, + pub enumerator: Option, + pub array: Option>, + pub object: Option, + pub record: Option>, + pub discriminator: Option, + pub nested_object: Option, + pub nested_array: Option>>, +} + +impl ArriModel for ObjectWithEveryOptionalType { + fn new() -> Self { + Self { + any: None, + boolean: None, + string: None, + timestamp: None, + float32: None, + float64: None, + int8: None, + uint8: None, + int16: None, + uint16: None, + int32: None, + uint32: None, + int64: None, + uint64: None, + enumerator: None, + array: None, + object: None, + record: None, + discriminator: None, + nested_object: None, + nested_array: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let any = match _val_.get("any") { + Some(any_val) => Some(any_val.to_owned()), + _ => None, + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => Some(boolean_val.to_owned()), + _ => None, + }; + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => Some(string_val.to_owned()), + _ => None, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + match DateTime::::parse_from_rfc3339(timestamp_val) { + Ok(timestamp_val_result) => Some(timestamp_val_result), + Err(_) => None, + } + } + _ => None, + }; + let float32 = match _val_.get("float32") { + Some(serde_json::Value::Number(float32_val)) => match float32_val.as_f64() { + Some(float32_val_result) => Some(float32_val_result as f32), + _ => None, + }, + _ => None, + }; + let float64 = match _val_.get("float64") { + Some(serde_json::Value::Number(float64_val)) => match float64_val.as_f64() { + Some(float64_val_result) => Some(float64_val_result), + _ => None, + }, + _ => None, + }; + let int8 = match _val_.get("int8") { + Some(serde_json::Value::Number(int8_val)) => match int8_val.as_i64() { + Some(int8_val_result) => match i8::try_from(int8_val_result) { + Ok(int8_val_result_val) => Some(int8_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint8 = match _val_.get("uint8") { + Some(serde_json::Value::Number(uint8_val)) => match uint8_val.as_u64() { + Some(uint8_val_result) => match u8::try_from(uint8_val_result) { + Ok(uint8_val_result_val) => Some(uint8_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int16 = match _val_.get("int16") { + Some(serde_json::Value::Number(int16_val)) => match int16_val.as_i64() { + Some(int16_val_result) => match i16::try_from(int16_val_result) { + Ok(int16_val_result_val) => Some(int16_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint16 = match _val_.get("uint16") { + Some(serde_json::Value::Number(uint16_val)) => match uint16_val.as_u64() { + Some(uint16_val_result) => match u16::try_from(uint16_val_result) { + Ok(uint16_val_result_val) => Some(uint16_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int32 = match _val_.get("int32") { + Some(serde_json::Value::Number(int32_val)) => match int32_val.as_i64() { + Some(int32_val_result) => match i32::try_from(int32_val_result) { + Ok(int32_val_result_val) => Some(int32_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let uint32 = match _val_.get("uint32") { + Some(serde_json::Value::Number(uint32_val)) => match uint32_val.as_u64() { + Some(uint32_val_result) => match u32::try_from(uint32_val_result) { + Ok(uint32_val_result_val) => Some(uint32_val_result_val), + Err(_) => None, + }, + _ => None, + }, + _ => None, + }; + let int64 = match _val_.get("int64") { + Some(serde_json::Value::String(int64_val)) => match int64_val.parse::() { + Ok(int64_val_result) => Some(int64_val_result), + Err(_) => None, + }, + _ => None, + }; + let uint64 = match _val_.get("uint64") { + Some(serde_json::Value::String(uint64_val)) => { + match uint64_val.parse::() { + Ok(uint64_val_result) => Some(uint64_val_result), + Err(_) => None, + } + } + _ => None, + }; + let enumerator = match _val_.get("enumerator") { + Some(serde_json::Value::String(enumerator_val)) => { + Some(ObjectWithEveryOptionalTypeEnumerator::from_string( + enumerator_val.to_owned(), + )) + } + _ => None, + }; + let array = match _val_.get("array") { + Some(serde_json::Value::Array(array_val)) => { + let mut array_val_result: Vec = Vec::new(); + for array_val_element in array_val { + array_val_result.push(match Some(array_val_element) { + Some(serde_json::Value::Bool(array_val_element_val)) => { + array_val_element_val.to_owned() + } + _ => false, + }); + } + Some(array_val_result) + } + _ => None, + }; + let object = match _val_.get("object") { + Some(object_val) => match object_val { + serde_json::Value::Object(_) => Some( + ObjectWithEveryOptionalTypeObject::from_json(object_val.to_owned()), + ), + _ => None, + }, + _ => None, + }; + let record = match _val_.get("record") { + Some(serde_json::Value::Object(record_val)) => { + let mut record_val_result: BTreeMap = BTreeMap::new(); + for (_key_, _value_) in record_val.into_iter() { + record_val_result.insert( + _key_.to_owned(), + match Some(_value_.to_owned()) { + Some(serde_json::Value::Bool(value_val)) => { + value_val.to_owned() + } + _ => false, + }, + ); + } + Some(record_val_result) + } + _ => None, + }; + let discriminator = match _val_.get("discriminator") { + Some(discriminator_val) => match discriminator_val { + serde_json::Value::Object(_) => { + Some(ObjectWithEveryOptionalTypeDiscriminator::from_json( + discriminator_val.to_owned(), + )) + } + _ => None, + }, + _ => None, + }; + let nested_object = match _val_.get("nestedObject") { + Some(nested_object_val) => match nested_object_val { + serde_json::Value::Object(_) => { + Some(ObjectWithEveryOptionalTypeNestedObject::from_json( + nested_object_val.to_owned(), + )) + } + _ => None, + }, + _ => None, + }; + let nested_array = match _val_.get("nestedArray") { + Some(serde_json::Value::Array(nested_array_val)) => { + let mut nested_array_val_result: Vec< + Vec, + > = Vec::new(); + for nested_array_val_element in nested_array_val { + nested_array_val_result.push(match Some(nested_array_val_element) { + Some(serde_json::Value::Array(nested_array_val_element_val)) => { + let mut nested_array_val_element_val_result: Vec = Vec::new(); + for nested_array_val_element_val_element in nested_array_val_element_val { + nested_array_val_element_val_result.push(match Some(nested_array_val_element_val_element) { + Some(nested_array_val_element_val_element_val) => ObjectWithEveryOptionalTypeNestedArrayElementElement::from_json(nested_array_val_element_val_element_val.to_owned()), + _ => ObjectWithEveryOptionalTypeNestedArrayElementElement::new(), + }); + } + nested_array_val_element_val_result + } + _ => Vec::new(), + }); + } + Some(nested_array_val_result) + } + _ => None, + }; + Self { + any, + boolean, + string, + timestamp, + float32, + float64, + int8, + uint8, + int16, + uint16, + int32, + uint32, + int64, + uint64, + enumerator, + array, + object, + record, + discriminator, + nested_object, + nested_array, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + let mut _has_keys_ = false; + match &self.any { + Some(any_val) => { + _json_output_.push_str("\"any\":"); + _json_output_.push_str( + serde_json::to_string(any_val) + .unwrap_or("null".to_string()) + .as_str(), + ); + _has_keys_ = true; + } + _ => {} + }; + match &self.boolean { + Some(boolean_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"boolean\":"); + _json_output_.push_str(boolean_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.string { + Some(string_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"string\":"); + _json_output_.push_str(serialize_string(string_val).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.timestamp { + Some(timestamp_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"timestamp\":"); + _json_output_.push_str(serialize_date_time(timestamp_val, true).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.float32 { + Some(float32_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"float32\":"); + _json_output_.push_str(float32_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.float64 { + Some(float64_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"float64\":"); + _json_output_.push_str(float64_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.int8 { + Some(int8_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"int8\":"); + _json_output_.push_str(int8_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.uint8 { + Some(uint8_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"uint8\":"); + _json_output_.push_str(uint8_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.int16 { + Some(int16_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"int16\":"); + _json_output_.push_str(int16_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.uint16 { + Some(uint16_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"uint16\":"); + _json_output_.push_str(uint16_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.int32 { + Some(int32_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"int32\":"); + _json_output_.push_str(int32_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.uint32 { + Some(uint32_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"uint32\":"); + _json_output_.push_str(uint32_val.to_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.int64 { + Some(int64_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"int64\":"); + _json_output_.push_str(format!("\"{}\"", int64_val).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.uint64 { + Some(uint64_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"uint64\":"); + _json_output_.push_str(format!("\"{}\"", uint64_val).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.enumerator { + Some(enumerator_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"enumerator\":"); + _json_output_.push_str(format!("\"{}\"", enumerator_val.serial_value()).as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.array { + Some(array_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"array\":"); + _json_output_.push('['); + for (_index_, _element_) in array_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_string().as_str()); + } + _json_output_.push(']'); + _has_keys_ = true; + } + _ => {} + }; + match &self.object { + Some(object_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"object\":"); + _json_output_.push_str(object_val.to_json_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.record { + Some(record_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"record\":"); + _json_output_.push('{'); + for (_index_, (_key_, _value_)) in record_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(format!("\"{}\":", _key_).as_str()); + _json_output_.push_str(_value_.to_string().as_str()); + } + _json_output_.push('}'); + _has_keys_ = true; + } + _ => {} + }; + match &self.discriminator { + Some(discriminator_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"discriminator\":"); + _json_output_.push_str(discriminator_val.to_json_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.nested_object { + Some(nested_object_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"nestedObject\":"); + _json_output_.push_str(nested_object_val.to_json_string().as_str()); + _has_keys_ = true; + } + _ => {} + }; + match &self.nested_array { + Some(nested_array_val) => { + if _has_keys_ { + _json_output_.push(','); + } + _json_output_.push_str("\"nestedArray\":"); + _json_output_.push('['); + for (_index_, _element_) in nested_array_val.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push('['); + for (_index_, _element_) in _element_.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_json_string().as_str()); + } + _json_output_.push(']'); + } + _json_output_.push(']'); + } + _ => {} + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + println!("[WARNING] cannot serialize any's to query params. Skipping field at /ObjectWithEveryOptionalType/any."); + match &self.boolean { + Some(boolean_val) => { + _query_parts_.push(format!("boolean={}", boolean_val)); + } + _ => {} + }; + match &self.string { + Some(string_val) => { + _query_parts_.push(format!("string={}", string_val)); + } + _ => {} + }; + match &self.timestamp { + Some(timestamp_val) => { + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(timestamp_val, false) + )); + } + _ => {} + }; + match &self.float32 { + Some(float32_val) => { + _query_parts_.push(format!("float32={}", float32_val)); + } + _ => {} + }; + match &self.float64 { + Some(float64_val) => { + _query_parts_.push(format!("float64={}", float64_val)); + } + _ => {} + }; + match &self.int8 { + Some(int8_val) => { + _query_parts_.push(format!("int8={}", int8_val)); + } + _ => {} + }; + match &self.uint8 { + Some(uint8_val) => { + _query_parts_.push(format!("uint8={}", uint8_val)); + } + _ => {} + }; + match &self.int16 { + Some(int16_val) => { + _query_parts_.push(format!("int16={}", int16_val)); + } + _ => {} + }; + match &self.uint16 { + Some(uint16_val) => { + _query_parts_.push(format!("uint16={}", uint16_val)); + } + _ => {} + }; + match &self.int32 { + Some(int32_val) => { + _query_parts_.push(format!("int32={}", int32_val)); + } + _ => {} + }; + match &self.uint32 { + Some(uint32_val) => { + _query_parts_.push(format!("uint32={}", uint32_val)); + } + _ => {} + }; + match &self.int64 { + Some(int64_val) => { + _query_parts_.push(format!("int64={}", int64_val)); + } + _ => {} + }; + match &self.uint64 { + Some(uint64_val) => { + _query_parts_.push(format!("uint64={}", uint64_val)); + } + _ => {} + }; + match &self.enumerator { + Some(enumerator_val) => { + _query_parts_.push(format!("enumerator={}", enumerator_val.serial_value())); + } + _ => {} + }; + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithEveryOptionalType/array."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryOptionalType/object."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryOptionalType/record."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryOptionalType/discriminator."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryOptionalType/nestedObject."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /ObjectWithEveryOptionalType/nestedArray."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ObjectWithEveryOptionalTypeEnumerator { + A, + B, + C, +} + +impl ArriEnum for ObjectWithEveryOptionalTypeEnumerator { + fn default() -> Self { + ObjectWithEveryOptionalTypeEnumerator::A + } + fn from_string(input: String) -> Self { + match input.as_str() { + "A" => Self::A, + "B" => Self::B, + "C" => Self::C, + _ => Self::default(), + } + } + fn serial_value(&self) -> String { + match &self { + ObjectWithEveryOptionalTypeEnumerator::A => "A".to_string(), + ObjectWithEveryOptionalTypeEnumerator::B => "B".to_string(), + ObjectWithEveryOptionalTypeEnumerator::C => "C".to_string(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryOptionalTypeObject { + pub string: String, + pub boolean: bool, + pub timestamp: DateTime, +} + +impl ArriModel for ObjectWithEveryOptionalTypeObject { + fn new() -> Self { + Self { + string: "".to_string(), + boolean: false, + timestamp: DateTime::default(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let string = match _val_.get("string") { + Some(serde_json::Value::String(string_val)) => string_val.to_owned(), + _ => "".to_string(), + }; + let boolean = match _val_.get("boolean") { + Some(serde_json::Value::Bool(boolean_val)) => boolean_val.to_owned(), + _ => false, + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + Self { + string, + boolean, + timestamp, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"string\":"); + _json_output_.push_str(serialize_string(&self.string).as_str()); + _json_output_.push_str(",\"boolean\":"); + _json_output_.push_str(&self.boolean.to_string().as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("string={}", &self.string)); + _query_parts_.push(format!("boolean={}", &self.boolean)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ObjectWithEveryOptionalTypeDiscriminator { + A { title: String }, + B { title: String, description: String }, +} + +impl ArriModel for ObjectWithEveryOptionalTypeDiscriminator { + fn new() -> Self { + Self::A { + title: "".to_string(), + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let r#type = match _val_.get("type") { + Some(serde_json::Value::String(r#type_val)) => r#type_val.to_owned(), + _ => "".to_string(), + }; + match r#type.as_str() { + "A" => { + let title = match _val_.get("title") { + Some(serde_json::Value::String(title_val)) => title_val.to_owned(), + _ => "".to_string(), + }; + Self::A { title } + } + "B" => { + let title = match _val_.get("title") { + Some(serde_json::Value::String(title_val)) => title_val.to_owned(), + _ => "".to_string(), + }; + let description = match _val_.get("description") { + Some(serde_json::Value::String(description_val)) => { + description_val.to_owned() + } + _ => "".to_string(), + }; + Self::B { title, description } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::A { title } => { + _json_output_.push_str("\"type\":\"A\""); + _json_output_.push_str(",\"title\":"); + _json_output_.push_str(serialize_string(title).as_str()); + } + Self::B { title, description } => { + _json_output_.push_str("\"type\":\"B\""); + _json_output_.push_str(",\"title\":"); + _json_output_.push_str(serialize_string(title).as_str()); + _json_output_.push_str(",\"description\":"); + _json_output_.push_str(serialize_string(description).as_str()); + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::A { title } => { + _query_parts_.push(format!("type=A")); + _query_parts_.push(format!("title={}", title)); + } + Self::B { title, description } => { + _query_parts_.push(format!("type=B")); + _query_parts_.push(format!("title={}", title)); + _query_parts_.push(format!("description={}", description)); + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryOptionalTypeNestedObject { + pub id: String, + pub timestamp: DateTime, + pub data: ObjectWithEveryOptionalTypeNestedObjectData, +} + +impl ArriModel for ObjectWithEveryOptionalTypeNestedObject { + fn new() -> Self { + Self { + id: "".to_string(), + timestamp: DateTime::default(), + data: ObjectWithEveryOptionalTypeNestedObjectData::new(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let data = match _val_.get("data") { + Some(data_val) => { + ObjectWithEveryOptionalTypeNestedObjectData::from_json(data_val.to_owned()) + } + _ => ObjectWithEveryOptionalTypeNestedObjectData::new(), + }; + Self { + id, + timestamp, + data, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push_str(",\"data\":"); + _json_output_.push_str(&self.data.to_json_string().as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryOptionalTypeNestedObject/data."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryOptionalTypeNestedObjectData { + pub id: String, + pub timestamp: DateTime, + pub data: ObjectWithEveryOptionalTypeNestedObjectDataData, +} + +impl ArriModel for ObjectWithEveryOptionalTypeNestedObjectData { + fn new() -> Self { + Self { + id: "".to_string(), + timestamp: DateTime::default(), + data: ObjectWithEveryOptionalTypeNestedObjectDataData::new(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let data = match _val_.get("data") { + Some(data_val) => ObjectWithEveryOptionalTypeNestedObjectDataData::from_json( + data_val.to_owned(), + ), + _ => ObjectWithEveryOptionalTypeNestedObjectDataData::new(), + }; + Self { + id, + timestamp, + data, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push_str(",\"data\":"); + _json_output_.push_str(&self.data.to_json_string().as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /ObjectWithEveryOptionalTypeNestedObjectData/data."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryOptionalTypeNestedObjectDataData { + pub id: String, + pub timestamp: DateTime, +} + +impl ArriModel for ObjectWithEveryOptionalTypeNestedObjectDataData { + fn new() -> Self { + Self { + id: "".to_string(), + timestamp: DateTime::default(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + Self { id, timestamp } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectWithEveryOptionalTypeNestedArrayElementElement { + pub id: String, + pub timestamp: DateTime, +} + +impl ArriModel for ObjectWithEveryOptionalTypeNestedArrayElementElement { + fn new() -> Self { + Self { + id: "".to_string(), + timestamp: DateTime::default(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let timestamp = match _val_.get("timestamp") { + Some(serde_json::Value::String(timestamp_val)) => { + DateTime::::parse_from_rfc3339(timestamp_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + Self { id, timestamp } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"timestamp\":"); + _json_output_.push_str(serialize_date_time(&self.timestamp, true).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!( + "timestamp={}", + serialize_date_time(&self.timestamp, false) + )); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RecursiveObject { + pub left: Option>, + pub right: Option>, + pub value: String, +} + +impl ArriModel for RecursiveObject { + fn new() -> Self { + Self { + left: None, + right: None, + value: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let left = match _val_.get("left") { + Some(left_val) => match left_val { + serde_json::Value::Object(_) => { + Some(Box::new(RecursiveObject::from_json(left_val.to_owned()))) + } + _ => None, + }, + _ => None, + }; + let right = match _val_.get("right") { + Some(right_val) => match right_val { + serde_json::Value::Object(_) => { + Some(Box::new(RecursiveObject::from_json(right_val.to_owned()))) + } + _ => None, + }, + _ => None, + }; + let value = match _val_.get("value") { + Some(serde_json::Value::String(value_val)) => value_val.to_owned(), + _ => "".to_string(), + }; + Self { left, right, value } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"left\":"); + match &self.left { + Some(left_val) => { + _json_output_.push_str(left_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"right\":"); + match &self.right { + Some(right_val) => { + _json_output_.push_str(right_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"value\":"); + _json_output_.push_str(serialize_string(&self.value).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /RecursiveObject/left."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /RecursiveObject/right."); + _query_parts_.push(format!("value={}", &self.value)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum RecursiveUnion { + /// Child node + Child { data: Box }, + /// List of children node + Children { data: Vec> }, + /// Text node + Text { data: String }, + /// Shape node + Shape { data: RecursiveUnionDataShape }, +} + +impl ArriModel for RecursiveUnion { + fn new() -> Self { + Self::Child { + data: Box::new(RecursiveUnion::new()), + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let r#type = match _val_.get("type") { + Some(serde_json::Value::String(r#type_val)) => r#type_val.to_owned(), + _ => "".to_string(), + }; + match r#type.as_str() { + "CHILD" => { + let data = match _val_.get("data") { + Some(data_val) => match data_val { + serde_json::Value::Object(_) => { + Box::new(RecursiveUnion::from_json(data_val.to_owned())) + } + _ => Box::new(RecursiveUnion::from_json(data_val.to_owned())), + }, + _ => Box::new(RecursiveUnion::new()), + }; + Self::Child { data } + } + "CHILDREN" => { + let data = match _val_.get("data") { + Some(serde_json::Value::Array(data_val)) => { + let mut data_val_result: Vec> = Vec::new(); + for data_val_element in data_val { + data_val_result.push(match Some(data_val_element) { + Some(data_val_element_val) => match data_val_element_val { + serde_json::Value::Object(_) => { + Box::new(RecursiveUnion::from_json( + data_val_element_val.to_owned(), + )) + } + _ => Box::new(RecursiveUnion::from_json( + data_val_element_val.to_owned(), + )), + }, + _ => Box::new(RecursiveUnion::new()), + }); + } + data_val_result + } + _ => Vec::new(), + }; + Self::Children { data } + } + "TEXT" => { + let data = match _val_.get("data") { + Some(serde_json::Value::String(data_val)) => data_val.to_owned(), + _ => "".to_string(), + }; + Self::Text { data } + } + "SHAPE" => { + let data = match _val_.get("data") { + Some(data_val) => { + RecursiveUnionDataShape::from_json(data_val.to_owned()) + } + _ => RecursiveUnionDataShape::new(), + }; + Self::Shape { data } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::Child { data } => { + _json_output_.push_str("\"type\":\"CHILD\""); + _json_output_.push_str(",\"data\":"); + _json_output_.push_str(data.to_json_string().as_str()); + } + Self::Children { data } => { + _json_output_.push_str("\"type\":\"CHILDREN\""); + _json_output_.push_str(",\"data\":"); + _json_output_.push('['); + for (_index_, _element_) in data.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_json_string().as_str()); + } + _json_output_.push(']'); + } + Self::Text { data } => { + _json_output_.push_str("\"type\":\"TEXT\""); + _json_output_.push_str(",\"data\":"); + _json_output_.push_str(serialize_string(data).as_str()); + } + Self::Shape { data } => { + _json_output_.push_str("\"type\":\"SHAPE\""); + _json_output_.push_str(",\"data\":"); + _json_output_.push_str(data.to_json_string().as_str()); + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::Child { data } => { + _query_parts_.push(format!("type=CHILD")); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at RecursiveUnion/data."); + } + Self::Children { data } => { + _query_parts_.push(format!("type=CHILDREN")); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at RecursiveUnion/data."); + } + Self::Text { data } => { + _query_parts_.push(format!("type=TEXT")); + _query_parts_.push(format!("data={}", data)); + } + Self::Shape { data } => { + _query_parts_.push(format!("type=SHAPE")); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at RecursiveUnion/data."); + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RecursiveUnionDataShape { + pub width: f64, + pub height: f64, + pub color: String, +} + +impl ArriModel for RecursiveUnionDataShape { + fn new() -> Self { + Self { + width: 0.0, + height: 0.0, + color: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let width = match _val_.get("width") { + Some(serde_json::Value::Number(width_val)) => width_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + let height = match _val_.get("height") { + Some(serde_json::Value::Number(height_val)) => { + height_val.as_f64().unwrap_or(0.0) + } + _ => 0.0, + }; + let color = match _val_.get("color") { + Some(serde_json::Value::String(color_val)) => color_val.to_owned(), + _ => "".to_string(), + }; + Self { + width, + height, + color, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"width\":"); + _json_output_.push_str(&self.width.to_string().as_str()); + _json_output_.push_str(",\"height\":"); + _json_output_.push_str(&self.height.to_string().as_str()); + _json_output_.push_str(",\"color\":"); + _json_output_.push_str(serialize_string(&self.color).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("width={}", &self.width)); + _query_parts_.push(format!("height={}", &self.height)); + _query_parts_.push(format!("color={}", &self.color)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AutoReconnectParams { + pub message_count: u8, +} + +impl ArriModel for AutoReconnectParams { + fn new() -> Self { + Self { message_count: 0 } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let message_count = match _val_.get("messageCount") { + Some(serde_json::Value::Number(message_count_val)) => { + u8::try_from(message_count_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + Self { message_count } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"messageCount\":"); + _json_output_.push_str(&self.message_count.to_string().as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("messageCount={}", &self.message_count)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AutoReconnectResponse { + pub count: u8, + pub message: String, +} + +impl ArriModel for AutoReconnectResponse { + fn new() -> Self { + Self { + count: 0, + message: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let count = match _val_.get("count") { + Some(serde_json::Value::Number(count_val)) => { + u8::try_from(count_val.as_u64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let message = match _val_.get("message") { + Some(serde_json::Value::String(message_val)) => message_val.to_owned(), + _ => "".to_string(), + }; + Self { count, message } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"count\":"); + _json_output_.push_str(&self.count.to_string().as_str()); + _json_output_.push_str(",\"message\":"); + _json_output_.push_str(serialize_string(&self.message).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("count={}", &self.count)); + _query_parts_.push(format!("message={}", &self.message)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct StreamConnectionErrorTestParams { + pub status_code: i32, + pub status_message: String, +} + +impl ArriModel for StreamConnectionErrorTestParams { + fn new() -> Self { + Self { + status_code: 0, + status_message: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let status_code = match _val_.get("statusCode") { + Some(serde_json::Value::Number(status_code_val)) => { + i32::try_from(status_code_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let status_message = match _val_.get("statusMessage") { + Some(serde_json::Value::String(status_message_val)) => { + status_message_val.to_owned() + } + _ => "".to_string(), + }; + Self { + status_code, + status_message, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"statusCode\":"); + _json_output_.push_str(&self.status_code.to_string().as_str()); + _json_output_.push_str(",\"statusMessage\":"); + _json_output_.push_str(serialize_string(&self.status_message).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("statusCode={}", &self.status_code)); + _query_parts_.push(format!("statusMessage={}", &self.status_message)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct StreamConnectionErrorTestResponse { + pub message: String, +} + +impl ArriModel for StreamConnectionErrorTestResponse { + fn new() -> Self { + Self { + message: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let message = match _val_.get("message") { + Some(serde_json::Value::String(message_val)) => message_val.to_owned(), + _ => "".to_string(), + }; + Self { message } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"message\":"); + _json_output_.push_str(serialize_string(&self.message).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("message={}", &self.message)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct StreamLargeObjectsResponse { + pub numbers: Vec, + pub objects: Vec, +} + +impl ArriModel for StreamLargeObjectsResponse { + fn new() -> Self { + Self { + numbers: Vec::new(), + objects: Vec::new(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let numbers = match _val_.get("numbers") { + Some(serde_json::Value::Array(numbers_val)) => { + let mut numbers_val_result: Vec = Vec::new(); + for numbers_val_element in numbers_val { + numbers_val_result.push(match Some(numbers_val_element) { + Some(serde_json::Value::Number(numbers_val_element_val)) => { + numbers_val_element_val.as_f64().unwrap_or(0.0) + } + _ => 0.0, + }); + } + numbers_val_result + } + _ => Vec::new(), + }; + let objects = match _val_.get("objects") { + Some(serde_json::Value::Array(objects_val)) => { + let mut objects_val_result: Vec = + Vec::new(); + for objects_val_element in objects_val { + objects_val_result.push(match Some(objects_val_element) { + Some(objects_val_element_val) => { + StreamLargeObjectsResponseObjectsElement::from_json( + objects_val_element_val.to_owned(), + ) + } + _ => StreamLargeObjectsResponseObjectsElement::new(), + }); + } + objects_val_result + } + _ => Vec::new(), + }; + Self { numbers, objects } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"numbers\":"); + _json_output_.push('['); + for (_index_, _element_) in self.numbers.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_string().as_str()); + } + _json_output_.push(']'); + _json_output_.push_str(",\"objects\":"); + _json_output_.push('['); + for (_index_, _element_) in self.objects.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_json_string().as_str()); + } + _json_output_.push(']'); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /StreamLargeObjectsResponse/numbers."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /StreamLargeObjectsResponse/objects."); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct StreamLargeObjectsResponseObjectsElement { + pub id: String, + pub name: String, + pub email: String, +} + +impl ArriModel for StreamLargeObjectsResponseObjectsElement { + fn new() -> Self { + Self { + id: "".to_string(), + name: "".to_string(), + email: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let name = match _val_.get("name") { + Some(serde_json::Value::String(name_val)) => name_val.to_owned(), + _ => "".to_string(), + }; + let email = match _val_.get("email") { + Some(serde_json::Value::String(email_val)) => email_val.to_owned(), + _ => "".to_string(), + }; + Self { id, name, email } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"name\":"); + _json_output_.push_str(serialize_string(&self.name).as_str()); + _json_output_.push_str(",\"email\":"); + _json_output_.push_str(serialize_string(&self.email).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!("name={}", &self.name)); + _query_parts_.push(format!("email={}", &self.email)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ChatMessageParams { + pub channel_id: String, +} + +impl ArriModel for ChatMessageParams { + fn new() -> Self { + Self { + channel_id: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let channel_id = match _val_.get("channelId") { + Some(serde_json::Value::String(channel_id_val)) => channel_id_val.to_owned(), + _ => "".to_string(), + }; + Self { channel_id } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"channelId\":"); + _json_output_.push_str(serialize_string(&self.channel_id).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("channelId={}", &self.channel_id)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ChatMessage { + Text { + id: String, + channel_id: String, + user_id: String, + date: DateTime, + text: String, + }, + Image { + id: String, + channel_id: String, + user_id: String, + date: DateTime, + image: String, + }, + Url { + id: String, + channel_id: String, + user_id: String, + date: DateTime, + url: String, + }, +} + +impl ArriModel for ChatMessage { + fn new() -> Self { + Self::Text { + id: "".to_string(), + channel_id: "".to_string(), + user_id: "".to_string(), + date: DateTime::default(), + text: "".to_string(), + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let message_type = match _val_.get("messageType") { + Some(serde_json::Value::String(message_type_val)) => { + message_type_val.to_owned() + } + _ => "".to_string(), + }; + match message_type.as_str() { + "TEXT" => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let channel_id = match _val_.get("channelId") { + Some(serde_json::Value::String(channel_id_val)) => { + channel_id_val.to_owned() + } + _ => "".to_string(), + }; + let user_id = match _val_.get("userId") { + Some(serde_json::Value::String(user_id_val)) => user_id_val.to_owned(), + _ => "".to_string(), + }; + let date = match _val_.get("date") { + Some(serde_json::Value::String(date_val)) => { + DateTime::::parse_from_rfc3339(date_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let text = match _val_.get("text") { + Some(serde_json::Value::String(text_val)) => text_val.to_owned(), + _ => "".to_string(), + }; + Self::Text { + id, + channel_id, + user_id, + date, + text, + } + } + "IMAGE" => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let channel_id = match _val_.get("channelId") { + Some(serde_json::Value::String(channel_id_val)) => { + channel_id_val.to_owned() + } + _ => "".to_string(), + }; + let user_id = match _val_.get("userId") { + Some(serde_json::Value::String(user_id_val)) => user_id_val.to_owned(), + _ => "".to_string(), + }; + let date = match _val_.get("date") { + Some(serde_json::Value::String(date_val)) => { + DateTime::::parse_from_rfc3339(date_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let image = match _val_.get("image") { + Some(serde_json::Value::String(image_val)) => image_val.to_owned(), + _ => "".to_string(), + }; + Self::Image { + id, + channel_id, + user_id, + date, + image, + } + } + "URL" => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let channel_id = match _val_.get("channelId") { + Some(serde_json::Value::String(channel_id_val)) => { + channel_id_val.to_owned() + } + _ => "".to_string(), + }; + let user_id = match _val_.get("userId") { + Some(serde_json::Value::String(user_id_val)) => user_id_val.to_owned(), + _ => "".to_string(), + }; + let date = match _val_.get("date") { + Some(serde_json::Value::String(date_val)) => { + DateTime::::parse_from_rfc3339(date_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let url = match _val_.get("url") { + Some(serde_json::Value::String(url_val)) => url_val.to_owned(), + _ => "".to_string(), + }; + Self::Url { + id, + channel_id, + user_id, + date, + url, + } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::Text { + id, + channel_id, + user_id, + date, + text, + } => { + _json_output_.push_str("\"messageType\":\"TEXT\""); + _json_output_.push_str(",\"id\":"); + _json_output_.push_str(serialize_string(id).as_str()); + _json_output_.push_str(",\"channelId\":"); + _json_output_.push_str(serialize_string(channel_id).as_str()); + _json_output_.push_str(",\"userId\":"); + _json_output_.push_str(serialize_string(user_id).as_str()); + _json_output_.push_str(",\"date\":"); + _json_output_.push_str(serialize_date_time(date, true).as_str()); + _json_output_.push_str(",\"text\":"); + _json_output_.push_str(serialize_string(text).as_str()); + } + Self::Image { + id, + channel_id, + user_id, + date, + image, + } => { + _json_output_.push_str("\"messageType\":\"IMAGE\""); + _json_output_.push_str(",\"id\":"); + _json_output_.push_str(serialize_string(id).as_str()); + _json_output_.push_str(",\"channelId\":"); + _json_output_.push_str(serialize_string(channel_id).as_str()); + _json_output_.push_str(",\"userId\":"); + _json_output_.push_str(serialize_string(user_id).as_str()); + _json_output_.push_str(",\"date\":"); + _json_output_.push_str(serialize_date_time(date, true).as_str()); + _json_output_.push_str(",\"image\":"); + _json_output_.push_str(serialize_string(image).as_str()); + } + Self::Url { + id, + channel_id, + user_id, + date, + url, + } => { + _json_output_.push_str("\"messageType\":\"URL\""); + _json_output_.push_str(",\"id\":"); + _json_output_.push_str(serialize_string(id).as_str()); + _json_output_.push_str(",\"channelId\":"); + _json_output_.push_str(serialize_string(channel_id).as_str()); + _json_output_.push_str(",\"userId\":"); + _json_output_.push_str(serialize_string(user_id).as_str()); + _json_output_.push_str(",\"date\":"); + _json_output_.push_str(serialize_date_time(date, true).as_str()); + _json_output_.push_str(",\"url\":"); + _json_output_.push_str(serialize_string(url).as_str()); + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::Text { + id, + channel_id, + user_id, + date, + text, + } => { + _query_parts_.push(format!("messageType=TEXT")); + _query_parts_.push(format!("id={}", id)); + _query_parts_.push(format!("channelId={}", channel_id)); + _query_parts_.push(format!("userId={}", user_id)); + _query_parts_.push(format!("date={}", serialize_date_time(date, false))); + _query_parts_.push(format!("text={}", text)); + } + Self::Image { + id, + channel_id, + user_id, + date, + image, + } => { + _query_parts_.push(format!("messageType=IMAGE")); + _query_parts_.push(format!("id={}", id)); + _query_parts_.push(format!("channelId={}", channel_id)); + _query_parts_.push(format!("userId={}", user_id)); + _query_parts_.push(format!("date={}", serialize_date_time(date, false))); + _query_parts_.push(format!("image={}", image)); + } + Self::Url { + id, + channel_id, + user_id, + date, + url, + } => { + _query_parts_.push(format!("messageType=URL")); + _query_parts_.push(format!("id={}", id)); + _query_parts_.push(format!("channelId={}", channel_id)); + _query_parts_.push(format!("userId={}", user_id)); + _query_parts_.push(format!("date={}", serialize_date_time(date, false))); + _query_parts_.push(format!("url={}", url)); + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct TestsStreamRetryWithNewCredentialsResponse { + pub message: String, +} + +impl ArriModel for TestsStreamRetryWithNewCredentialsResponse { + fn new() -> Self { + Self { + message: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let message = match _val_.get("message") { + Some(serde_json::Value::String(message_val)) => message_val.to_owned(), + _ => "".to_string(), + }; + Self { message } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"message\":"); + _json_output_.push_str(serialize_string(&self.message).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("message={}", &self.message)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum WsMessageParams { + CreateEntity { entity_id: String, x: f64, y: f64 }, + UpdateEntity { entity_id: String, x: f64, y: f64 }, + Disconnect { reason: String }, +} + +impl ArriModel for WsMessageParams { + fn new() -> Self { + Self::CreateEntity { + entity_id: "".to_string(), + x: 0.0, + y: 0.0, + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let r#type = match _val_.get("type") { + Some(serde_json::Value::String(r#type_val)) => r#type_val.to_owned(), + _ => "".to_string(), + }; + match r#type.as_str() { + "CREATE_ENTITY" => { + let entity_id = match _val_.get("entityId") { + Some(serde_json::Value::String(entity_id_val)) => { + entity_id_val.to_owned() + } + _ => "".to_string(), + }; + let x = match _val_.get("x") { + Some(serde_json::Value::Number(x_val)) => x_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + let y = match _val_.get("y") { + Some(serde_json::Value::Number(y_val)) => y_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + Self::CreateEntity { entity_id, x, y } + } + "UPDATE_ENTITY" => { + let entity_id = match _val_.get("entityId") { + Some(serde_json::Value::String(entity_id_val)) => { + entity_id_val.to_owned() + } + _ => "".to_string(), + }; + let x = match _val_.get("x") { + Some(serde_json::Value::Number(x_val)) => x_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + let y = match _val_.get("y") { + Some(serde_json::Value::Number(y_val)) => y_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + Self::UpdateEntity { entity_id, x, y } + } + "DISCONNECT" => { + let reason = match _val_.get("reason") { + Some(serde_json::Value::String(reason_val)) => reason_val.to_owned(), + _ => "".to_string(), + }; + Self::Disconnect { reason } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::CreateEntity { entity_id, x, y } => { + _json_output_.push_str("\"type\":\"CREATE_ENTITY\""); + _json_output_.push_str(",\"entityId\":"); + _json_output_.push_str(serialize_string(entity_id).as_str()); + _json_output_.push_str(",\"x\":"); + _json_output_.push_str(x.to_string().as_str()); + _json_output_.push_str(",\"y\":"); + _json_output_.push_str(y.to_string().as_str()); + } + Self::UpdateEntity { entity_id, x, y } => { + _json_output_.push_str("\"type\":\"UPDATE_ENTITY\""); + _json_output_.push_str(",\"entityId\":"); + _json_output_.push_str(serialize_string(entity_id).as_str()); + _json_output_.push_str(",\"x\":"); + _json_output_.push_str(x.to_string().as_str()); + _json_output_.push_str(",\"y\":"); + _json_output_.push_str(y.to_string().as_str()); + } + Self::Disconnect { reason } => { + _json_output_.push_str("\"type\":\"DISCONNECT\""); + _json_output_.push_str(",\"reason\":"); + _json_output_.push_str(serialize_string(reason).as_str()); + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::CreateEntity { entity_id, x, y } => { + _query_parts_.push(format!("type=CREATE_ENTITY")); + _query_parts_.push(format!("entityId={}", entity_id)); + _query_parts_.push(format!("x={}", x)); + _query_parts_.push(format!("y={}", y)); + } + Self::UpdateEntity { entity_id, x, y } => { + _query_parts_.push(format!("type=UPDATE_ENTITY")); + _query_parts_.push(format!("entityId={}", entity_id)); + _query_parts_.push(format!("x={}", x)); + _query_parts_.push(format!("y={}", y)); + } + Self::Disconnect { reason } => { + _query_parts_.push(format!("type=DISCONNECT")); + _query_parts_.push(format!("reason={}", reason)); + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum WsMessageResponse { + EntityCreated { entity_id: String, x: f64, y: f64 }, + EntityUpdated { entity_id: String, x: f64, y: f64 }, +} + +impl ArriModel for WsMessageResponse { + fn new() -> Self { + Self::EntityCreated { + entity_id: "".to_string(), + x: 0.0, + y: 0.0, + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let r#type = match _val_.get("type") { + Some(serde_json::Value::String(r#type_val)) => r#type_val.to_owned(), + _ => "".to_string(), + }; + match r#type.as_str() { + "ENTITY_CREATED" => { + let entity_id = match _val_.get("entityId") { + Some(serde_json::Value::String(entity_id_val)) => { + entity_id_val.to_owned() + } + _ => "".to_string(), + }; + let x = match _val_.get("x") { + Some(serde_json::Value::Number(x_val)) => x_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + let y = match _val_.get("y") { + Some(serde_json::Value::Number(y_val)) => y_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + Self::EntityCreated { entity_id, x, y } + } + "ENTITY_UPDATED" => { + let entity_id = match _val_.get("entityId") { + Some(serde_json::Value::String(entity_id_val)) => { + entity_id_val.to_owned() + } + _ => "".to_string(), + }; + let x = match _val_.get("x") { + Some(serde_json::Value::Number(x_val)) => x_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + let y = match _val_.get("y") { + Some(serde_json::Value::Number(y_val)) => y_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + Self::EntityUpdated { entity_id, x, y } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::EntityCreated { entity_id, x, y } => { + _json_output_.push_str("\"type\":\"ENTITY_CREATED\""); + _json_output_.push_str(",\"entityId\":"); + _json_output_.push_str(serialize_string(entity_id).as_str()); + _json_output_.push_str(",\"x\":"); + _json_output_.push_str(x.to_string().as_str()); + _json_output_.push_str(",\"y\":"); + _json_output_.push_str(y.to_string().as_str()); + } + Self::EntityUpdated { entity_id, x, y } => { + _json_output_.push_str("\"type\":\"ENTITY_UPDATED\""); + _json_output_.push_str(",\"entityId\":"); + _json_output_.push_str(serialize_string(entity_id).as_str()); + _json_output_.push_str(",\"x\":"); + _json_output_.push_str(x.to_string().as_str()); + _json_output_.push_str(",\"y\":"); + _json_output_.push_str(y.to_string().as_str()); + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::EntityCreated { entity_id, x, y } => { + _query_parts_.push(format!("type=ENTITY_CREATED")); + _query_parts_.push(format!("entityId={}", entity_id)); + _query_parts_.push(format!("x={}", x)); + _query_parts_.push(format!("y={}", y)); + } + Self::EntityUpdated { entity_id, x, y } => { + _query_parts_.push(format!("type=ENTITY_UPDATED")); + _query_parts_.push(format!("entityId={}", entity_id)); + _query_parts_.push(format!("x={}", x)); + _query_parts_.push(format!("y={}", y)); + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct UsersWatchUserParams { + pub user_id: String, +} + +impl ArriModel for UsersWatchUserParams { + fn new() -> Self { + Self { + user_id: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let user_id = match _val_.get("userId") { + Some(serde_json::Value::String(user_id_val)) => user_id_val.to_owned(), + _ => "".to_string(), + }; + Self { user_id } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"userId\":"); + _json_output_.push_str(serialize_string(&self.user_id).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("userId={}", &self.user_id)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct UsersWatchUserResponse { + pub id: String, + pub role: UsersWatchUserResponseRole, + /// A profile picture + pub photo: Option, + pub created_at: DateTime, + pub num_followers: i32, + pub settings: UserSettings, + pub recent_notifications: Vec, + pub bookmarks: BTreeMap, + pub metadata: BTreeMap, + pub random_list: Vec, + pub bio: Option, +} + +impl ArriModel for UsersWatchUserResponse { + fn new() -> Self { + Self { + id: "".to_string(), + role: UsersWatchUserResponseRole::default(), + photo: None, + created_at: DateTime::default(), + num_followers: 0, + settings: UserSettings::new(), + recent_notifications: Vec::new(), + bookmarks: BTreeMap::new(), + metadata: BTreeMap::new(), + random_list: Vec::new(), + bio: None, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let id = match _val_.get("id") { + Some(serde_json::Value::String(id_val)) => id_val.to_owned(), + _ => "".to_string(), + }; + let role = match _val_.get("role") { + Some(serde_json::Value::String(role_val)) => { + UsersWatchUserResponseRole::from_string(role_val.to_owned()) + } + _ => UsersWatchUserResponseRole::default(), + }; + let photo = match _val_.get("photo") { + Some(photo_val) => match photo_val { + serde_json::Value::Object(_) => { + Some(UserPhoto::from_json(photo_val.to_owned())) + } + _ => None, + }, + _ => None, + }; + let created_at = match _val_.get("createdAt") { + Some(serde_json::Value::String(created_at_val)) => { + DateTime::::parse_from_rfc3339(created_at_val) + .unwrap_or(DateTime::default()) + } + _ => DateTime::default(), + }; + let num_followers = match _val_.get("numFollowers") { + Some(serde_json::Value::Number(num_followers_val)) => { + i32::try_from(num_followers_val.as_i64().unwrap_or(0)).unwrap_or(0) + } + _ => 0, + }; + let settings = match _val_.get("settings") { + Some(settings_val) => UserSettings::from_json(settings_val.to_owned()), + _ => UserSettings::new(), + }; + let recent_notifications = match _val_.get("recentNotifications") { + Some(serde_json::Value::Array(recent_notifications_val)) => { + let mut recent_notifications_val_result: Vec< + UsersWatchUserResponseRecentNotificationsElement, + > = Vec::new(); + for recent_notifications_val_element in recent_notifications_val { + recent_notifications_val_result.push(match Some(recent_notifications_val_element) { + Some(recent_notifications_val_element_val) => match recent_notifications_val_element_val { + serde_json::Value::Object(_) => { + UsersWatchUserResponseRecentNotificationsElement::from_json(recent_notifications_val_element_val.to_owned()) + } + _ => UsersWatchUserResponseRecentNotificationsElement::new(), + }, + _ => UsersWatchUserResponseRecentNotificationsElement::new(), + }); + } + recent_notifications_val_result + } + _ => Vec::new(), + }; + let bookmarks = match _val_.get("bookmarks") { + Some(serde_json::Value::Object(bookmarks_val)) => { + let mut bookmarks_val_result: BTreeMap< + String, + UsersWatchUserResponseBookmarksValue, + > = BTreeMap::new(); + for (_key_, _value_) in bookmarks_val.into_iter() { + bookmarks_val_result.insert( + _key_.to_owned(), + match Some(_value_.to_owned()) { + Some(value_val) => { + UsersWatchUserResponseBookmarksValue::from_json( + value_val.to_owned(), + ) + } + _ => UsersWatchUserResponseBookmarksValue::new(), + }, + ); + } + bookmarks_val_result + } + _ => BTreeMap::new(), + }; + let metadata = match _val_.get("metadata") { + Some(serde_json::Value::Object(metadata_val)) => { + let mut metadata_val_result: BTreeMap = + BTreeMap::new(); + for (_key_, _value_) in metadata_val.into_iter() { + metadata_val_result.insert( + _key_.to_owned(), + match Some(_value_.to_owned()) { + Some(value_val) => value_val.to_owned(), + _ => serde_json::Value::Null, + }, + ); + } + metadata_val_result + } + _ => BTreeMap::new(), + }; + let random_list = match _val_.get("randomList") { + Some(serde_json::Value::Array(random_list_val)) => { + let mut random_list_val_result: Vec = Vec::new(); + for random_list_val_element in random_list_val { + random_list_val_result.push(match Some(random_list_val_element) { + Some(random_list_val_element_val) => { + random_list_val_element_val.to_owned() + } + _ => serde_json::Value::Null, + }); + } + random_list_val_result + } + _ => Vec::new(), + }; + let bio = match _val_.get("bio") { + Some(serde_json::Value::String(bio_val)) => Some(bio_val.to_owned()), + _ => None, + }; + Self { + id, + role, + photo, + created_at, + num_followers, + settings, + recent_notifications, + bookmarks, + metadata, + random_list, + bio, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"id\":"); + _json_output_.push_str(serialize_string(&self.id).as_str()); + _json_output_.push_str(",\"role\":"); + _json_output_.push_str(format!("\"{}\"", &self.role.serial_value()).as_str()); + _json_output_.push_str(",\"photo\":"); + match &self.photo { + Some(photo_val) => { + _json_output_.push_str(photo_val.to_json_string().as_str()); + } + _ => { + _json_output_.push_str("null"); + } + }; + _json_output_.push_str(",\"createdAt\":"); + _json_output_.push_str(serialize_date_time(&self.created_at, true).as_str()); + _json_output_.push_str(",\"numFollowers\":"); + _json_output_.push_str(&self.num_followers.to_string().as_str()); + _json_output_.push_str(",\"settings\":"); + _json_output_.push_str(&self.settings.to_json_string().as_str()); + _json_output_.push_str(",\"recentNotifications\":"); + _json_output_.push('['); + for (_index_, _element_) in self.recent_notifications.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(_element_.to_json_string().as_str()); + } + _json_output_.push(']'); + _json_output_.push_str(",\"bookmarks\":"); + _json_output_.push('{'); + for (_index_, (_key_, _value_)) in self.bookmarks.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(format!("\"{}\":", _key_).as_str()); + _json_output_.push_str(_value_.to_json_string().as_str()); + } + _json_output_.push('}'); + _json_output_.push_str(",\"metadata\":"); + _json_output_.push('{'); + for (_index_, (_key_, _value_)) in self.metadata.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str(format!("\"{}\":", _key_).as_str()); + _json_output_.push_str( + serde_json::to_string(_value_) + .unwrap_or("null".to_string()) + .as_str(), + ); + } + _json_output_.push('}'); + _json_output_.push_str(",\"randomList\":"); + _json_output_.push('['); + for (_index_, _element_) in self.random_list.iter().enumerate() { + if _index_ != 0 { + _json_output_.push(','); + } + _json_output_.push_str( + serde_json::to_string(_element_) + .unwrap_or("null".to_string()) + .as_str(), + ); + } + _json_output_.push(']'); + match &self.bio { + Some(bio_val) => { + _json_output_.push_str(",\"bio\":"); + _json_output_.push_str(serialize_string(bio_val).as_str()) + } + _ => {} + }; + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("id={}", &self.id)); + _query_parts_.push(format!("role={}", &self.role.serial_value())); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /UsersWatchUserResponse/photo."); + _query_parts_.push(format!( + "createdAt={}", + serialize_date_time(&self.created_at, false) + )); + _query_parts_.push(format!("numFollowers={}", &self.num_followers)); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /UsersWatchUserResponse/settings."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /UsersWatchUserResponse/recentNotifications."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /UsersWatchUserResponse/bookmarks."); + println!("[WARNING] cannot serialize nested objects to query params. Skipping field at /UsersWatchUserResponse/metadata."); + println!("[WARNING] cannot serialize arrays to query params. Skipping field at /UsersWatchUserResponse/randomList."); + match &self.bio { + Some(bio_val) => { + _query_parts_.push(format!("bio={}", bio_val)); + } + _ => {} + }; + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum UsersWatchUserResponseRole { + Standard, + Admin, +} + +impl ArriEnum for UsersWatchUserResponseRole { + fn default() -> Self { + UsersWatchUserResponseRole::Standard + } + fn from_string(input: String) -> Self { + match input.as_str() { + "standard" => Self::Standard, + "admin" => Self::Admin, + _ => Self::default(), + } + } + fn serial_value(&self) -> String { + match &self { + UsersWatchUserResponseRole::Standard => "standard".to_string(), + UsersWatchUserResponseRole::Admin => "admin".to_string(), + } + } +} + +/// A profile picture +#[derive(Clone, Debug, PartialEq)] +pub struct UserPhoto { + pub url: String, + pub width: f64, + pub height: f64, + pub bytes: i64, + /// When the photo was last updated in nanoseconds + pub nanoseconds: u64, +} + +impl ArriModel for UserPhoto { + fn new() -> Self { + Self { + url: "".to_string(), + width: 0.0, + height: 0.0, + bytes: 0, + nanoseconds: 0, + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let url = match _val_.get("url") { + Some(serde_json::Value::String(url_val)) => url_val.to_owned(), + _ => "".to_string(), + }; + let width = match _val_.get("width") { + Some(serde_json::Value::Number(width_val)) => width_val.as_f64().unwrap_or(0.0), + _ => 0.0, + }; + let height = match _val_.get("height") { + Some(serde_json::Value::Number(height_val)) => { + height_val.as_f64().unwrap_or(0.0) + } + _ => 0.0, + }; + let bytes = match _val_.get("bytes") { + Some(serde_json::Value::String(bytes_val)) => { + bytes_val.parse::().unwrap_or(0) + } + _ => 0, + }; + let nanoseconds = match _val_.get("nanoseconds") { + Some(serde_json::Value::String(nanoseconds_val)) => { + nanoseconds_val.parse::().unwrap_or(0) + } + _ => 0, + }; + Self { + url, + width, + height, + bytes, + nanoseconds, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"url\":"); + _json_output_.push_str(serialize_string(&self.url).as_str()); + _json_output_.push_str(",\"width\":"); + _json_output_.push_str(&self.width.to_string().as_str()); + _json_output_.push_str(",\"height\":"); + _json_output_.push_str(&self.height.to_string().as_str()); + _json_output_.push_str(",\"bytes\":"); + _json_output_.push_str(format!("\"{}\"", &self.bytes).as_str()); + _json_output_.push_str(",\"nanoseconds\":"); + _json_output_.push_str(format!("\"{}\"", &self.nanoseconds).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("url={}", &self.url)); + _query_parts_.push(format!("width={}", &self.width)); + _query_parts_.push(format!("height={}", &self.height)); + _query_parts_.push(format!("bytes={}", &self.bytes)); + _query_parts_.push(format!("nanoseconds={}", &self.nanoseconds)); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct UserSettings { + pub notifications_enabled: bool, + pub preferred_theme: UserSettingsPreferredTheme, +} + +impl ArriModel for UserSettings { + fn new() -> Self { + Self { + notifications_enabled: false, + preferred_theme: UserSettingsPreferredTheme::default(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let notifications_enabled = match _val_.get("notificationsEnabled") { + Some(serde_json::Value::Bool(notifications_enabled_val)) => { + notifications_enabled_val.to_owned() + } + _ => false, + }; + let preferred_theme = match _val_.get("preferredTheme") { + Some(serde_json::Value::String(preferred_theme_val)) => { + UserSettingsPreferredTheme::from_string(preferred_theme_val.to_owned()) + } + _ => UserSettingsPreferredTheme::default(), + }; + Self { + notifications_enabled, + preferred_theme, + } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"notificationsEnabled\":"); + _json_output_.push_str(&self.notifications_enabled.to_string().as_str()); + _json_output_.push_str(",\"preferredTheme\":"); + _json_output_.push_str(format!("\"{}\"", &self.preferred_theme.serial_value()).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!( + "notificationsEnabled={}", + &self.notifications_enabled + )); + _query_parts_.push(format!( + "preferredTheme={}", + &self.preferred_theme.serial_value() + )); + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum UserSettingsPreferredTheme { + DarkMode, + LightMode, + System, +} + +impl ArriEnum for UserSettingsPreferredTheme { + fn default() -> Self { + UserSettingsPreferredTheme::DarkMode + } + fn from_string(input: String) -> Self { + match input.as_str() { + "dark-mode" => Self::DarkMode, + "light-mode" => Self::LightMode, + "system" => Self::System, + _ => Self::default(), + } + } + fn serial_value(&self) -> String { + match &self { + UserSettingsPreferredTheme::DarkMode => "dark-mode".to_string(), + UserSettingsPreferredTheme::LightMode => "light-mode".to_string(), + UserSettingsPreferredTheme::System => "system".to_string(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum UsersWatchUserResponseRecentNotificationsElement { + PostLike { + post_id: String, + user_id: String, + }, + PostComment { + post_id: String, + user_id: String, + comment_text: String, + }, +} + +impl ArriModel for UsersWatchUserResponseRecentNotificationsElement { + fn new() -> Self { + Self::PostLike { + post_id: "".to_string(), + user_id: "".to_string(), + } + } + + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let notification_type = match _val_.get("notificationType") { + Some(serde_json::Value::String(notification_type_val)) => { + notification_type_val.to_owned() + } + _ => "".to_string(), + }; + match notification_type.as_str() { + "POST_LIKE" => { + let post_id = match _val_.get("postId") { + Some(serde_json::Value::String(post_id_val)) => post_id_val.to_owned(), + _ => "".to_string(), + }; + let user_id = match _val_.get("userId") { + Some(serde_json::Value::String(user_id_val)) => user_id_val.to_owned(), + _ => "".to_string(), + }; + Self::PostLike { post_id, user_id } + } + "POST_COMMENT" => { + let post_id = match _val_.get("postId") { + Some(serde_json::Value::String(post_id_val)) => post_id_val.to_owned(), + _ => "".to_string(), + }; + let user_id = match _val_.get("userId") { + Some(serde_json::Value::String(user_id_val)) => user_id_val.to_owned(), + _ => "".to_string(), + }; + let comment_text = match _val_.get("commentText") { + Some(serde_json::Value::String(comment_text_val)) => { + comment_text_val.to_owned() + } + _ => "".to_string(), + }; + Self::PostComment { + post_id, + user_id, + comment_text, + } + } + _ => Self::new(), + } + } + _ => Self::new(), + } + } + + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + match &self { + Self::PostLike { post_id, user_id } => { + _json_output_.push_str("\"notificationType\":\"POST_LIKE\""); + _json_output_.push_str(",\"postId\":"); + _json_output_.push_str(serialize_string(post_id).as_str()); + _json_output_.push_str(",\"userId\":"); + _json_output_.push_str(serialize_string(user_id).as_str()); + } + Self::PostComment { + post_id, + user_id, + comment_text, + } => { + _json_output_.push_str("\"notificationType\":\"POST_COMMENT\""); + _json_output_.push_str(",\"postId\":"); + _json_output_.push_str(serialize_string(post_id).as_str()); + _json_output_.push_str(",\"userId\":"); + _json_output_.push_str(serialize_string(user_id).as_str()); + _json_output_.push_str(",\"commentText\":"); + _json_output_.push_str(serialize_string(comment_text).as_str()); + } + } + _json_output_.push('}'); + _json_output_ + } + + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + match &self { + Self::PostLike { post_id, user_id } => { + _query_parts_.push(format!("notificationType=POST_LIKE")); + _query_parts_.push(format!("postId={}", post_id)); + _query_parts_.push(format!("userId={}", user_id)); + } + Self::PostComment { + post_id, + user_id, + comment_text, + } => { + _query_parts_.push(format!("notificationType=POST_COMMENT")); + _query_parts_.push(format!("postId={}", post_id)); + _query_parts_.push(format!("userId={}", user_id)); + _query_parts_.push(format!("commentText={}", comment_text)); + } + } + _query_parts_.join("&") + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct UsersWatchUserResponseBookmarksValue { + pub post_id: String, + pub user_id: String, +} + +impl ArriModel for UsersWatchUserResponseBookmarksValue { + fn new() -> Self { + Self { + post_id: "".to_string(), + user_id: "".to_string(), + } + } + fn from_json(input: serde_json::Value) -> Self { + match input { + serde_json::Value::Object(_val_) => { + let post_id = match _val_.get("postId") { + Some(serde_json::Value::String(post_id_val)) => post_id_val.to_owned(), + _ => "".to_string(), + }; + let user_id = match _val_.get("userId") { + Some(serde_json::Value::String(user_id_val)) => user_id_val.to_owned(), + _ => "".to_string(), + }; + Self { post_id, user_id } + } + _ => Self::new(), + } + } + fn from_json_string(input: String) -> Self { + match serde_json::from_str(input.as_str()) { + Ok(val) => Self::from_json(val), + _ => Self::new(), + } + } + fn to_json_string(&self) -> String { + let mut _json_output_ = "{".to_string(); + + _json_output_.push_str("\"postId\":"); + _json_output_.push_str(serialize_string(&self.post_id).as_str()); + _json_output_.push_str(",\"userId\":"); + _json_output_.push_str(serialize_string(&self.user_id).as_str()); + _json_output_.push('}'); + _json_output_ + } + fn to_query_params_string(&self) -> String { + let mut _query_parts_: Vec = Vec::new(); + _query_parts_.push(format!("postId={}", &self.post_id)); + _query_parts_.push(format!("userId={}", &self.user_id)); + _query_parts_.join("&") + } +} diff --git a/tests/clients/ts/project.json b/tests/clients/ts/project.json index 0ac0dd10..8d22da71 100644 --- a/tests/clients/ts/project.json +++ b/tests/clients/ts/project.json @@ -1,7 +1,7 @@ { "name": "test-client-ts", "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "implicitDependencies": ["test-server"], + "implicitDependencies": ["test-server", "ts-client"], "targets": { "integration-test": { "executor": "@nx/vite:test", diff --git a/tests/clients/ts/testClient.rpc.ts b/tests/clients/ts/testClient.rpc.ts index 04741d5b..02439dcb 100644 --- a/tests/clients/ts/testClient.rpc.ts +++ b/tests/clients/ts/testClient.rpc.ts @@ -112,6 +112,17 @@ export class TestClientTestsService { clientVersion: this.clientVersion, }); } + sendError(params: SendErrorParams) { + return arriRequest({ + url: `${this.baseUrl}/rpcs/tests/send-error`, + method: "post", + headers: this.headers, + params, + parser: (_) => {}, + serializer: $$SendErrorParams.serialize, + clientVersion: this.clientVersion, + }); + } sendObject(params: ObjectWithEveryType) { return arriRequest({ url: `${this.baseUrl}/rpcs/tests/send-object`, @@ -1100,6 +1111,139 @@ export const $$DeprecatedRpcParams = { }, }; +export interface SendErrorParams { + code: number; + message: string; +} +export const $$SendErrorParams = { + parse(input: Record): SendErrorParams { + function $fallback(instancePath, schemaPath) { + throw new Error( + `Error parsing input. InstancePath: "${instancePath}". SchemaPath: "${schemaPath}"`, + ); + } + + if (typeof input === "string") { + const json = JSON.parse(input); + let result = {}; + if (typeof json === "object" && json !== null) { + const __D1 = {}; + if ( + typeof json.code === "number" && + Number.isInteger(json.code) && + json.code >= 0 && + json.code <= 65535 + ) { + __D1.code = json.code; + } else { + $fallback( + "/code", + "/properties/code", + "Expected valid integer between 0 and 65535", + ); + } + if (typeof json.message === "string") { + __D1.message = json.message; + } else { + $fallback( + "/message", + "/properties/message/type", + "Expected string at /message", + ); + } + result = __D1; + } else { + $fallback("", "", "Expected object"); + } + return result; + } + let result = {}; + if (typeof input === "object" && input !== null) { + const __D1 = {}; + if ( + typeof input.code === "number" && + Number.isInteger(input.code) && + input.code >= 0 && + input.code <= 65535 + ) { + __D1.code = input.code; + } else { + $fallback( + "/code", + "/properties/code", + "Expected valid integer between 0 and 65535", + ); + } + if (typeof input.message === "string") { + __D1.message = input.message; + } else { + $fallback( + "/message", + "/properties/message/type", + "Expected string at /message", + ); + } + result = __D1; + } else { + $fallback("", "", "Expected object"); + } + return result; + }, + serialize(input: SendErrorParams): string { + let json = ""; + + const STR_ESCAPE = + /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/; + + json += ""; + json += "{"; + + if (Number.isNaN(input.code)) { + throw new Error("Expected number at /code got NaN"); + } + json += `"code":${input.code}`; + json += `,"message":`; + if (input.message.length < 42) { + let __result__ = ""; + let __last__ = -1; + let __point__ = 255; + let __finished__ = false; + for (let i = 0; i < input.message.length; i++) { + __point__ = input.message.charCodeAt(i); + if ( + __point__ < 32 || + (__point__ >= 0xd800 && __point__ <= 0xdfff) + ) { + json += JSON.stringify(input.message); + __finished__ = true; + break; + } + if (__point__ === 0x22 || __point__ === 0x5c) { + __last__ === -1 && (__last__ = 0); + __result__ += input.message.slice(__last__, i) + "\\"; + __last__ = i; + } + } + if (!__finished__) { + if (__last__ === -1) { + json += `"${input.message}"`; + } else { + json += `"${__result__}${input.message.slice(__last__)}"`; + } + } + } else if ( + input.message.length < 5000 && + !STR_ESCAPE.test(input.message) + ) { + json += `"${input.message}"`; + } else { + json += JSON.stringify(input.message); + } + json += "}"; + return json; + }, +}; + export interface ObjectWithEveryType { any: any; boolean: boolean; @@ -8340,21 +8484,33 @@ export const $$RecursiveUnion = { return json; }, }; +/** + * Child node + */ export interface RecursiveUnionChild { type: "CHILD"; data: RecursiveUnion; } +/** + * List of children node + */ export interface RecursiveUnionChildren { type: "CHILDREN"; data: Array; } +/** + * Text node + */ export interface RecursiveUnionText { type: "TEXT"; data: string; } +/** + * Shape node + */ export interface RecursiveUnionShape { type: "SHAPE"; data: RecursiveUnionShapeData; diff --git a/tests/server/arri.config.ts b/tests/server/arri.config.ts index 57734ebe..8f19ce38 100644 --- a/tests/server/arri.config.ts +++ b/tests/server/arri.config.ts @@ -37,5 +37,12 @@ export default defineConfig({ "../clients/kotlin/src/main/kotlin/TestClient.rpc.kt", ), }), + generators.rustClient({ + clientName: "TestClient", + outputFile: path.resolve( + __dirname, + "../clients/rust/src/test_client.g.rs", + ), + }), ], }); diff --git a/tests/server/src/app.ts b/tests/server/src/app.ts index 23cf1ab5..cf992469 100644 --- a/tests/server/src/app.ts +++ b/tests/server/src/app.ts @@ -22,7 +22,7 @@ const app = new ArriApp({ }); app.use( - defineMiddleware((event) => { + defineMiddleware(async (event) => { const authHeader = getHeader(event, "x-test-header"); if ( !authHeader?.length && diff --git a/tests/server/src/procedures/tests/sendError.rpc.ts b/tests/server/src/procedures/tests/sendError.rpc.ts new file mode 100644 index 00000000..0c705231 --- /dev/null +++ b/tests/server/src/procedures/tests/sendError.rpc.ts @@ -0,0 +1,13 @@ +import { a } from "@arrirpc/schema"; +import { defineError, defineRpc } from "@arrirpc/server"; + +export default defineRpc({ + params: a.object("SendErrorParams", { + code: a.uint16(), + message: a.string(), + }), + response: undefined, + handler({ params }) { + throw defineError(params.code, { message: params.message }); + }, +}); diff --git a/tests/server/src/procedures/tests/sendRecursiveUnion.rpc.ts b/tests/server/src/procedures/tests/sendRecursiveUnion.rpc.ts index 2b8a5676..0c33729b 100644 --- a/tests/server/src/procedures/tests/sendRecursiveUnion.rpc.ts +++ b/tests/server/src/procedures/tests/sendRecursiveUnion.rpc.ts @@ -12,22 +12,38 @@ type RecursiveUnion = const RecursiveUnion = a.recursive("RecursiveUnion", (self) => a.discriminator("type", { - CHILD: a.object({ - data: self, - }), - CHILDREN: a.object({ - data: a.array(self), - }), - TEXT: a.object({ - data: a.string(), - }), - SHAPE: a.object({ - data: a.object({ - width: a.float64(), - height: a.float64(), - color: a.string(), - }), - }), + CHILD: a.object( + { + data: self, + }, + { description: "Child node" }, + ), + CHILDREN: a.object( + { + data: a.array(self), + }, + { description: "List of children node" }, + ), + TEXT: a.object( + { + data: a.string(), + }, + { + description: "Text node", + }, + ), + SHAPE: a.object( + { + data: a.object({ + width: a.float64(), + height: a.float64(), + color: a.string(), + }), + }, + { + description: "Shape node", + }, + ), }), ); diff --git a/tooling/cli/package.json b/tooling/cli/package.json index 23a4c5ce..43d52e54 100644 --- a/tooling/cli/package.json +++ b/tooling/cli/package.json @@ -18,6 +18,7 @@ "dependencies": { "@arrirpc/codegen-dart": "workspace:*", "@arrirpc/codegen-kotlin": "workspace:*", + "@arrirpc/codegen-rust": "workspace:*", "@arrirpc/codegen-ts": "workspace:*", "@arrirpc/codegen-utils": "workspace:*", "@arrirpc/schema": "workspace:*", diff --git a/tooling/cli/src/_index.ts b/tooling/cli/src/_index.ts index 27520625..a77ab11f 100644 --- a/tooling/cli/src/_index.ts +++ b/tooling/cli/src/_index.ts @@ -1,5 +1,6 @@ import { dartClientGenerator } from "@arrirpc/codegen-dart"; import { kotlinClientGenerator } from "@arrirpc/codegen-kotlin"; +import { rustClientGenerator } from "@arrirpc/codegen-rust"; import { typescriptClientGenerator } from "@arrirpc/codegen-ts"; export { DEV_DEFINITION_ENDPOINT } from "./commands/dev"; @@ -12,5 +13,6 @@ export { export const generators = { dartClient: dartClientGenerator, kotlinClient: kotlinClientGenerator, + rustClient: rustClientGenerator, typescriptClient: typescriptClientGenerator, } as const;