diff --git a/packages/typespec-rust/.scripts/tspcompile.js b/packages/typespec-rust/.scripts/tspcompile.js index 6f792d8fc..dde014175 100644 --- a/packages/typespec-rust/.scripts/tspcompile.js +++ b/packages/typespec-rust/.scripts/tspcompile.js @@ -154,6 +154,9 @@ function should_generate(name) { return true } +// When https://github.com/Azure/typespec-azure/pull/3950 is merged, and we use the newer version of @azure-tools/azure-http-specs, +// we can remove alternate_types from below, add it to azureHttpSpecsGroup above, and remove the checked-in tsp files +// from packages\typespec-rust\test\spector\azure\client-generator-core\alternate-type\. const alternate_types = pkgRoot + 'test/tsp/AlternateTypes'; generate('alternate_types', alternate_types, 'test/other/alternate_types'); @@ -190,6 +193,9 @@ generate('pub_crate', pub_crate, 'test/other/pub_crate'); const client_option = pkgRoot + 'test/tsp/ClientOption'; generate('client_option', client_option, 'test/other/client_option'); +const spector_alternatetype = pkgRoot + 'test/spector/azure/client-generator-core/alternate-type/client.tsp'; +generate('spector_alternatetype', spector_alternatetype, 'test/spector/azure/client-generator-core/alternate-type'); + loopSpec(httpSpecsGroup, httpSpecs) loopSpec(azureHttpSpecsGroup, azureHttpSpecs) diff --git a/packages/typespec-rust/src/emitter.ts b/packages/typespec-rust/src/emitter.ts index 2839a2498..e641072ba 100644 --- a/packages/typespec-rust/src/emitter.ts +++ b/packages/typespec-rust/src/emitter.ts @@ -25,7 +25,7 @@ export async function $onEmit(context: EmitContext) { let failed = false; try { const adapter = await Adapter.create(context); - const crate = adapter.tcgcToCrate(); + const crate = adapter.tcgcToCrate(context); await mkdir(`${context.emitterOutputDir}/src`, { recursive: true }); diff --git a/packages/typespec-rust/src/tcgcadapter/adapter.ts b/packages/typespec-rust/src/tcgcadapter/adapter.ts index 833a5bb9d..b5124c851 100644 --- a/packages/typespec-rust/src/tcgcadapter/adapter.ts +++ b/packages/typespec-rust/src/tcgcadapter/adapter.ts @@ -14,6 +14,7 @@ import * as utils from '../utils/utils.js'; import * as tcgc from '@azure-tools/typespec-client-generator-core'; import * as rust from '../codemodel/index.js'; import {FinalStateValue} from "@azure-tools/typespec-azure-core"; +import {EmitContext, NoTarget} from "@typespec/compiler"; /** ErrorCode defines the types of adapter errors */ export type ErrorCode = @@ -114,8 +115,8 @@ export class Adapter { } /** performs all the steps to convert tcgc to a crate */ - tcgcToCrate(): rust.Crate { - this.adaptTypes(); + tcgcToCrate(context: EmitContext): rust.Crate { + this.adaptTypes(context); this.adaptClients(); // marker models don't require serde so exclude them from the check @@ -146,10 +147,17 @@ export class Adapter { } /** converts all tcgc types to their Rust type equivalent */ - private adaptTypes(): void { + private adaptTypes(context: EmitContext): void { for (const sdkUnion of this.ctx.sdkPackage.unions.filter(u => u.kind === 'union')) { if (!sdkUnion.discriminatedOptions) { - throw new AdapterError('UnsupportedTsp', 'non-discriminated unions are not supported', sdkUnion.__raw?.node); + // When https://github.com/microsoft/typespec/issues/9749 is fixed, we can return to throwing an exception here. + context.program.reportDiagnostic({ + code: 'UnsupportedTsp', + severity: 'warning', + message: `Non-discriminated unions ('${sdkUnion.name}') are not supported. No type will be generated.`, + target: NoTarget, + }) + continue; } const rustUnion = this.getDiscriminatedUnion(sdkUnion); this.crate.unions.push(rustUnion); diff --git a/packages/typespec-rust/test/Cargo.lock b/packages/typespec-rust/test/Cargo.lock index acce552de..fd41154f1 100644 --- a/packages/typespec-rust/test/Cargo.lock +++ b/packages/typespec-rust/test/Cargo.lock @@ -37,12 +37,33 @@ dependencies = [ "serde", ] +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "as-slice" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" +dependencies = [ + "generic-array 0.12.4", + "generic-array 0.13.3", + "generic-array 0.14.9", + "stable_deref_trait", +] + [[package]] name = "async-compression" version = "0.4.32" @@ -78,6 +99,15 @@ dependencies = [ "syn 2.0.107", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -207,6 +237,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.11.1" @@ -304,6 +340,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -537,6 +579,63 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "geo-types" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f8647af4005fa11da47cd56252c6ef030be8fa97bdbf355e7dfb6348f0a82c" +dependencies = [ + "approx", + "num-traits", + "rstar 0.10.0", + "rstar 0.11.0", + "rstar 0.12.2", + "rstar 0.8.4", + "rstar 0.9.3", + "serde", +] + +[[package]] +name = "geojson" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e26f3c45b36fccc9cf2805e61d4da6bc4bbd5a3a9589b01afa3a40eff703bd79" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -560,6 +659,33 @@ dependencies = [ "wasip2", ] +[[package]] +name = "hash32" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -575,6 +701,41 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +[[package]] +name = "heapless" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" +dependencies = [ + "as-slice", + "generic-array 0.14.9", + "hash32 0.1.1", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32 0.2.1", + "rustc_version", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + [[package]] name = "http" version = "1.3.1" @@ -840,6 +1001,12 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -852,6 +1019,15 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.28" @@ -931,6 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -989,6 +1166,12 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +[[package]] +name = "pdqselect" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1268,6 +1451,67 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rstar" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a45c0e8804d37e4d97e55c6f258bc9ad9c5ee7b07437009dd152d764949a27c" +dependencies = [ + "heapless 0.6.1", + "num-traits", + "pdqselect", + "serde", + "smallvec", +] + +[[package]] +name = "rstar" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40f1bfe5acdab44bc63e6699c28b74f75ec43afb59f3eda01e145aff86a25fa" +dependencies = [ + "heapless 0.7.17", + "num-traits", + "serde", + "smallvec", +] + +[[package]] +name = "rstar" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f39465655a1e3d8ae79c6d9e007f4953bfc5d55297602df9dc38f9ae9f1359a" +dependencies = [ + "heapless 0.7.17", + "num-traits", + "serde", + "smallvec", +] + +[[package]] +name = "rstar" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73111312eb7a2287d229f06c00ff35b51ddee180f017ab6dec1f69d62ac098d6" +dependencies = [ + "heapless 0.7.17", + "num-traits", + "serde", + "smallvec", +] + +[[package]] +name = "rstar" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb" +dependencies = [ + "heapless 0.8.0", + "num-traits", + "serde", + "smallvec", +] + [[package]] name = "rust_decimal" version = "1.39.0" @@ -1336,6 +1580,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "seahash" version = "4.1.0" @@ -1464,6 +1714,17 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spector_alternatetype" +version = "0.1.0" +dependencies = [ + "azure_core", + "geojson", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "spector_apikey" version = "0.1.0" @@ -2267,6 +2528,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -2334,6 +2604,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + [[package]] name = "time" version = "0.3.47" @@ -2557,6 +2847,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "typespec" version = "0.12.0" diff --git a/packages/typespec-rust/test/Cargo.toml b/packages/typespec-rust/test/Cargo.toml index 76d5b7a06..6fc765d0d 100644 --- a/packages/typespec-rust/test/Cargo.toml +++ b/packages/typespec-rust/test/Cargo.toml @@ -19,6 +19,7 @@ members = [ "spector/azure/client-generator-core/api-version/header", "spector/azure/client-generator-core/api-version/path", "spector/azure/client-generator-core/api-version/query", + "spector/azure/client-generator-core/alternate-type", "spector/azure/client-generator-core/client-initialization/default", "spector/azure/client-generator-core/client-initialization/individually", "spector/azure/client-generator-core/client-initialization/individuallyParent", @@ -117,6 +118,7 @@ azure_core = { git = "https://github.com/Azure/azure-sdk-for-rust.git", rev = "6 ] } bytes = "1.11.1" futures = "0.3.31" +geojson = "0.24.2" rust_decimal = "1.37.2" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.142" diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/Cargo.toml b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/Cargo.toml new file mode 100644 index 000000000..169d43b99 --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "spector_alternatetype" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[features] +default = ["azure_core/default"] + +[dependencies] +azure_core = { workspace = true } +geojson = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true } diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/client.tsp b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/client.tsp new file mode 100644 index 000000000..a3b42ddbd --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/client.tsp @@ -0,0 +1,64 @@ +import "@azure-tools/typespec-client-generator-core"; +import "./main.tsp"; + +using Azure.ClientGenerator.Core; + +@@global.Azure.ClientGenerator.Core.clientNamespace(_Specs_.Azure.ClientGenerator.Core.AlternateType, + "azure.clientgenerator.core.alternatetype", + "java" +); + +@@alternateType(_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.Feature, + { + identity: "geojson.Feature", + package: "geojson", + minVersion: "3.2.0", + }, + "python" +); + +@@alternateType(_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.Feature, + { + identity: "com.azure.core.models.GeoObject", // cSpell:ignore GeoObject + package: "com.azure.core", + minVersion: "1.14.0", + }, + "java" +); + +@@alternateType(_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.Feature, + { + identity: "NetTopologySuite.IO.GeoJSON.Feature", + package: "NetTopologySuite.IO.GeoJSON", + minVersion: "4.0.0", + }, + "csharp" +); + +@@alternateType(_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.Feature, + { + identity: "NetTopologySuite.IO.GeoJSON.Feature", + package: "@types/geojson", + minVersion: "7946.0.0", + }, + "typescript" +); + +@@alternateType(_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.Feature, + { + identity: "geojson::Feature", + package: "geojson", + minVersion: "0.24.2", + }, + "rust" +); + +// When https://github.com/microsoft/typespec/issues/9749 is fixed, the statement below is no longer needed. +@@alternateType(_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.Geometry, + { + identity: "geojson::Geometry", + package: "geojson", + minVersion: "0.24.2", + }, + "rust" +); diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/main.tsp b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/main.tsp new file mode 100644 index 000000000..ba27cca21 --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/main.tsp @@ -0,0 +1,139 @@ +import "@typespec/http"; +import "@typespec/spector"; +import "@azure-tools/typespec-client-generator-core"; + +using Http; +using Azure.ClientGenerator.Core; +using Spector; + +@doc("Test for alternate type decorator") +@scenarioService("/azure/client-generator-core/alternate-type") +@service +namespace _Specs_.Azure.ClientGenerator.Core.AlternateType; + +@global.Azure.ClientGenerator.Core.operationGroup +@route("/external") +@doc("Test using language-idiomatic external types for defined TypeSpec types") +namespace ExternalType { + model Geometry { + type: string; + coordinates: numeric[]; + } + + model Feature { + type: "Feature"; + geometry: Geometry | null; + properties: Record; + id?: string | numeric; + } + + model ModelWithFeatureProperty { + feature: Feature; + additionalProperty: string; + } + @scenario + @scenarioDoc(""" + Input: None + Output: Feature object with geometry, properties, and optional id fields. + Example response: + ```json + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-122.25, 37.87] + }, + "properties": { + "name": "A single point of interest", + "category": "landmark", + "elevation": 100 + }, + "id": "feature-1" + } + ``` + """) + @route("/model") + @get + op getModel(): Feature; + + @scenario + @scenarioDoc(""" + Input: Feature object in request body. + Example input: + ```json + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-122.25, 37.87] + }, + "properties": { + "name": "A single point of interest", + "category": "landmark", + "elevation": 100 + }, + "id": "feature-1" + } + ``` + Output: None (204/empty response) + """) + @route("/model") + @put + op putModel(@body body: Feature): void; + + @scenario + @scenarioDoc(""" + Input: None + Output: ModelWithFeatureProperty object with feature and additionalProperty fields. + Example response: + ```json + { + "feature": { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-122.25, 37.87] + }, + "properties": { + "name": "A single point of interest", + "category": "landmark", + "elevation": 100 + }, + "id": "feature-1" + }, + "additionalProperty": "extra" + } + ``` + """) + @route("/property") + @get + op getProperty(): ModelWithFeatureProperty; + + @scenario + @scenarioDoc(""" + Input: ModelWithFeatureProperty object in request body. + Example input: + ```json + { + "feature": { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-122.25, 37.87] + }, + "properties": { + "name": "A single point of interest", + "category": "landmark", + "elevation": 100 + }, + "id": "feature-1" + }, + "additionalProperty": "extra" + } + ``` + Output: None (204/empty response) + """) + @route("/property") + @put + op putProperty(@body body: ModelWithFeatureProperty): void; +} diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/alternate_type_client.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/alternate_type_client.rs new file mode 100644 index 000000000..f9c29a29e --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/alternate_type_client.rs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. + +use crate::generated::clients::AlternateTypeExternalTypeClient; +use azure_core::{ + fmt::SafeDebug, + http::{ClientOptions, Pipeline, Url}, + tracing, Result, +}; + +/// Test for alternate type decorator +#[tracing::client] +pub struct AlternateTypeClient { + pub(crate) endpoint: Url, + pub(crate) pipeline: Pipeline, +} + +/// Options used when creating a [`AlternateTypeClient`](AlternateTypeClient) +#[derive(Clone, Default, SafeDebug)] +pub struct AlternateTypeClientOptions { + /// Allows customization of the client. + pub client_options: ClientOptions, +} + +impl AlternateTypeClient { + /// Creates a new AlternateTypeClient requiring no authentication. + /// + /// # Arguments + /// + /// * `endpoint` - Service host + /// * `options` - Optional configuration for the client. + #[tracing::new("_Specs_.Azure.ClientGenerator.Core.AlternateType")] + pub fn with_no_credential( + endpoint: &str, + options: Option, + ) -> Result { + let options = options.unwrap_or_default(); + let endpoint = Url::parse(endpoint)?; + if !endpoint.scheme().starts_with("http") { + return Err(azure_core::Error::with_message( + azure_core::error::ErrorKind::Other, + format!("{endpoint} must use http(s)"), + )); + } + Ok(Self { + endpoint, + pipeline: Pipeline::new( + option_env!("CARGO_PKG_NAME"), + option_env!("CARGO_PKG_VERSION"), + options.client_options, + Vec::default(), + Vec::default(), + None, + ), + }) + } + + /// Returns the Url associated with this client. + pub fn endpoint(&self) -> &Url { + &self.endpoint + } + + /// Returns a new instance of AlternateTypeExternalTypeClient. + #[tracing::subclient] + pub fn get_alternate_type_external_type_client(&self) -> AlternateTypeExternalTypeClient { + AlternateTypeExternalTypeClient { + endpoint: self.endpoint.clone(), + pipeline: self.pipeline.clone(), + } + } +} diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/alternate_type_external_type_client.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/alternate_type_external_type_client.rs new file mode 100644 index 000000000..37f30f8aa --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/alternate_type_external_type_client.rs @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. + +use crate::generated::models::{ + AlternateTypeExternalTypeClientGetModelOptions, + AlternateTypeExternalTypeClientGetPropertyOptions, + AlternateTypeExternalTypeClientPutModelOptions, + AlternateTypeExternalTypeClientPutPropertyOptions, ModelWithFeatureProperty, +}; +use azure_core::{ + error::CheckSuccessOptions, + http::{ + Method, NoFormat, Pipeline, PipelineSendOptions, Request, RequestContent, Response, Url, + UrlExt, + }, + tracing, Result, +}; +use geojson::Feature; + +/// Test using language-idiomatic external types for defined TypeSpec types +#[tracing::client] +pub struct AlternateTypeExternalTypeClient { + pub(crate) endpoint: Url, + pub(crate) pipeline: Pipeline, +} + +impl AlternateTypeExternalTypeClient { + /// Returns the Url associated with this client. + pub fn endpoint(&self) -> &Url { + &self.endpoint + } + + /// + /// # Arguments + /// + /// * `options` - Optional parameters for the request. + #[tracing::function("_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.getModel")] + pub async fn get_model( + &self, + options: Option>, + ) -> Result> { + let options = options.unwrap_or_default(); + let ctx = options.method_options.context.to_borrowed(); + let mut url = self.endpoint.clone(); + url.append_path("/azure/client-generator-core/alternate-type/external/model"); + let mut request = Request::new(url, Method::Get); + request.insert_header("accept", "application/json"); + let rsp = self + .pipeline + .send( + &ctx, + &mut request, + Some(PipelineSendOptions { + check_success: CheckSuccessOptions { + success_codes: &[200], + }, + ..Default::default() + }), + ) + .await?; + Ok(rsp.into()) + } + + /// + /// # Arguments + /// + /// * `options` - Optional parameters for the request. + #[tracing::function( + "_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.getProperty" + )] + pub async fn get_property( + &self, + options: Option>, + ) -> Result> { + let options = options.unwrap_or_default(); + let ctx = options.method_options.context.to_borrowed(); + let mut url = self.endpoint.clone(); + url.append_path("/azure/client-generator-core/alternate-type/external/property"); + let mut request = Request::new(url, Method::Get); + request.insert_header("accept", "application/json"); + let rsp = self + .pipeline + .send( + &ctx, + &mut request, + Some(PipelineSendOptions { + check_success: CheckSuccessOptions { + success_codes: &[200], + }, + ..Default::default() + }), + ) + .await?; + Ok(rsp.into()) + } + + /// + /// # Arguments + /// + /// * `options` - Optional parameters for the request. + #[tracing::function("_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.putModel")] + pub async fn put_model( + &self, + body: RequestContent, + options: Option>, + ) -> Result> { + let options = options.unwrap_or_default(); + let ctx = options.method_options.context.to_borrowed(); + let mut url = self.endpoint.clone(); + url.append_path("/azure/client-generator-core/alternate-type/external/model"); + let mut request = Request::new(url, Method::Put); + request.insert_header("content-type", "application/json"); + request.set_body(body); + let rsp = self + .pipeline + .send( + &ctx, + &mut request, + Some(PipelineSendOptions { + check_success: CheckSuccessOptions { + success_codes: &[204], + }, + ..Default::default() + }), + ) + .await?; + Ok(rsp.into()) + } + + /// + /// # Arguments + /// + /// * `options` - Optional parameters for the request. + #[tracing::function( + "_Specs_.Azure.ClientGenerator.Core.AlternateType.ExternalType.putProperty" + )] + pub async fn put_property( + &self, + body: RequestContent, + options: Option>, + ) -> Result> { + let options = options.unwrap_or_default(); + let ctx = options.method_options.context.to_borrowed(); + let mut url = self.endpoint.clone(); + url.append_path("/azure/client-generator-core/alternate-type/external/property"); + let mut request = Request::new(url, Method::Put); + request.insert_header("content-type", "application/json"); + request.set_body(body); + let rsp = self + .pipeline + .send( + &ctx, + &mut request, + Some(PipelineSendOptions { + check_success: CheckSuccessOptions { + success_codes: &[204], + }, + ..Default::default() + }), + ) + .await?; + Ok(rsp.into()) + } +} diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/mod.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/mod.rs new file mode 100644 index 000000000..214766457 --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/clients/mod.rs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. + +mod alternate_type_client; +mod alternate_type_external_type_client; +pub use alternate_type_client::*; +pub use alternate_type_external_type_client::*; diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/mod.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/mod.rs new file mode 100644 index 000000000..debd6e390 --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/mod.rs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. + +/// Clients used to communicate with the service. +pub mod clients; +/// Contains all the data structures and types used by the client library. +pub mod models; +pub use clients::{AlternateTypeClient, AlternateTypeClientOptions}; diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/method_options.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/method_options.rs new file mode 100644 index 000000000..f67487314 --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/method_options.rs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. + +use azure_core::{fmt::SafeDebug, http::ClientMethodOptions}; + +/// Options to be passed to [`AlternateTypeExternalTypeClient::get_model()`](crate::generated::clients::AlternateTypeExternalTypeClient::get_model()) +#[derive(Clone, Default, SafeDebug)] +pub struct AlternateTypeExternalTypeClientGetModelOptions<'a> { + /// Allows customization of the method call. + pub method_options: ClientMethodOptions<'a>, +} + +/// Options to be passed to [`AlternateTypeExternalTypeClient::get_property()`](crate::generated::clients::AlternateTypeExternalTypeClient::get_property()) +#[derive(Clone, Default, SafeDebug)] +pub struct AlternateTypeExternalTypeClientGetPropertyOptions<'a> { + /// Allows customization of the method call. + pub method_options: ClientMethodOptions<'a>, +} + +/// Options to be passed to [`AlternateTypeExternalTypeClient::put_model()`](crate::generated::clients::AlternateTypeExternalTypeClient::put_model()) +#[derive(Clone, Default, SafeDebug)] +pub struct AlternateTypeExternalTypeClientPutModelOptions<'a> { + /// Allows customization of the method call. + pub method_options: ClientMethodOptions<'a>, +} + +/// Options to be passed to [`AlternateTypeExternalTypeClient::put_property()`](crate::generated::clients::AlternateTypeExternalTypeClient::put_property()) +#[derive(Clone, Default, SafeDebug)] +pub struct AlternateTypeExternalTypeClientPutPropertyOptions<'a> { + /// Allows customization of the method call. + pub method_options: ClientMethodOptions<'a>, +} diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/mod.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/mod.rs new file mode 100644 index 000000000..5dfb21b69 --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/mod.rs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. + +mod method_options; +#[allow(clippy::module_inception)] +mod models; +mod models_impl; +pub use method_options::*; +pub use models::*; diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/models.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/models.rs new file mode 100644 index 000000000..045d0136f --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/models.rs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. + +use azure_core::fmt::SafeDebug; +use geojson::Feature; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Deserialize, SafeDebug, Serialize)] +pub struct ModelWithFeatureProperty { + #[serde(rename = "additionalProperty", skip_serializing_if = "Option::is_none")] + pub additional_property: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub feature: Option, +} diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/models_impl.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/models_impl.rs new file mode 100644 index 000000000..a21606ab9 --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/generated/models/models_impl.rs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. + +use super::ModelWithFeatureProperty; +use azure_core::{http::RequestContent, json::to_json, Result}; + +impl TryFrom for RequestContent { + type Error = azure_core::Error; + fn try_from(value: ModelWithFeatureProperty) -> Result { + Ok(to_json(&value)?.into()) + } +} diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/lib.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/lib.rs new file mode 100644 index 000000000..cc784e401 --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/src/lib.rs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// Code generated by Microsoft (R) Rust Code Generator. + +#![cfg_attr(docsrs, feature(doc_cfg))] + +mod generated; +pub use generated::*; diff --git a/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/tests/external_type_client.rs b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/tests/external_type_client.rs new file mode 100644 index 000000000..127b5f36f --- /dev/null +++ b/packages/typespec-rust/test/spector/azure/client-generator-core/alternate-type/tests/external_type_client.rs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the MIT License. See License.txt in the project root for license information. + +use azure_core::http::RequestContent; +use geojson::{Feature, Geometry, Value}; +use serde_json::Map; +use spector_alternatetype::{models::ModelWithFeatureProperty, AlternateTypeClient}; + +fn create_feature() -> Feature { + let geometry = Geometry::new(Value::Point(vec![-122.25, 37.87])); + let mut properties = Map::new(); + properties.insert( + "name".to_string(), + serde_json::Value::String("A single point of interest".to_string()), + ); + properties.insert( + "category".to_string(), + serde_json::Value::String("landmark".to_string()), + ); + properties.insert( + "elevation".to_string(), + serde_json::Value::Number(100.into()), + ); + + Feature { + bbox: None, + geometry: Some(geometry), + id: Some(geojson::feature::Id::String("feature-1".to_string())), + properties: Some(properties), + foreign_members: None, + } +} + +#[tokio::test] +async fn get_model() { + let client = AlternateTypeClient::with_no_credential("http://localhost:3000", None).unwrap(); + let resp = client + .get_alternate_type_external_type_client() + .get_model(None) + .await + .unwrap(); + let feature: Feature = resp.into_model().unwrap(); + assert!(feature.geometry.is_some()); + assert_eq!( + feature.id, + Some(geojson::feature::Id::String("feature-1".to_string())) + ); +} + +#[tokio::test] +async fn put_model() { + let client = AlternateTypeClient::with_no_credential("http://localhost:3000", None).unwrap(); + let feature = create_feature(); + let json = serde_json::to_string(&feature).unwrap(); + client + .get_alternate_type_external_type_client() + .put_model(RequestContent::from_str(&json), None) + .await + .unwrap(); +} + +#[tokio::test] +async fn get_property() { + let client = AlternateTypeClient::with_no_credential("http://localhost:3000", None).unwrap(); + let resp = client + .get_alternate_type_external_type_client() + .get_property(None) + .await + .unwrap(); + let model: ModelWithFeatureProperty = resp.into_model().unwrap(); + assert!(model.feature.is_some()); + assert_eq!(model.additional_property, Some("extra".to_string())); +} + +#[tokio::test] +async fn put_property() { + let client = AlternateTypeClient::with_no_credential("http://localhost:3000", None).unwrap(); + let feature = create_feature(); + let model = ModelWithFeatureProperty { + feature: Some(feature), + additional_property: Some("extra".to_string()), + }; + client + .get_alternate_type_external_type_client() + .put_property(model.try_into().unwrap(), None) + .await + .unwrap(); +}