Skip to main content

forge_ir_bindgen/
convert.rs

1//! Conversions between bindgen-generated WIT types and [`forge_ir`].
2//!
3//! Both `ir-transformer` and `code-generator` worlds share the `types`,
4//! `host-api`, and `stage` interfaces, but `wasmtime::component::bindgen!`
5//! emits *separate* Rust modules per world (the types are structurally
6//! identical, nominally distinct). To avoid duplicating ~600 lines of
7//! conversion glue, we expand the conversions once per world via a
8//! `macro_rules!` macro keyed on the bindings root module.
9//!
10//! Public API:
11//!
12//! * `convert::transformer::*` — operates on
13//!   `crate::bindings::transformer::forge::plugin::types::*`.
14//! * `convert::generator::*` — operates on
15//!   `crate::bindings::generator::forge::plugin::types::*`.
16//!
17//! Both submodules expose:
18//!
19//! * `ir_to_wit(forge_ir::Ir) -> b::types::Ir`
20//! * `ir_from_wit(b::types::Ir) -> Result<forge_ir::Ir, BindgenError>`
21//! * `diagnostic_from_wit(b::types::Diagnostic) -> Result<forge_ir::Diagnostic, BindgenError>`
22//! * `plugin_info_from_wit(b::types::PluginInfo) -> forge_ir::PluginInfo`
23//! * `stage_error_from_wit(b::stage::StageError) -> StageErrorRepr`
24//!
25//! `StageErrorRepr` is a world-independent representation of the WIT
26//! `stage-error` variant; the host translates it to its own `StageError`
27//! type to avoid a circular dependency.
28
29use forge_ir as ir;
30
31/// Host-neutral representation of the WIT `stage-error` variant. Shared
32/// between both worlds; the host wraps it into its own `StageError`.
33#[derive(Debug, Clone, PartialEq)]
34pub enum StageErrorRepr {
35    Rejected {
36        reason: String,
37        diagnostics: Vec<ir::Diagnostic>,
38    },
39    PluginBug(String),
40    ConfigInvalid(String),
41    ResourceExceeded(ResourceKindRepr),
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum ResourceKindRepr {
46    Fuel,
47    Memory,
48    Time,
49    OutputSize,
50}
51
52macro_rules! define_world_conversions {
53    ($mod:ident, $world:ident) => {
54        pub mod $mod {
55            //! Conversions between `forge_ir` and the
56            #![doc = concat!(" `", stringify!($world), "` world's WIT types.")]
57
58            use super::{ResourceKindRepr, StageErrorRepr};
59            use crate::bindings::$world::forge::plugin::{stage as b_stage, types as b};
60            use crate::BindgenError;
61            use forge_ir as ir;
62
63            // -------------------------------------------------------------
64            // PluginInfo
65            // -------------------------------------------------------------
66
67            pub fn plugin_info_from_wit(p: b::PluginInfo) -> ir::PluginInfo {
68                ir::PluginInfo {
69                    name: p.name,
70                    version: p.version,
71                }
72            }
73
74            pub fn plugin_info_to_wit(p: ir::PluginInfo) -> b::PluginInfo {
75                b::PluginInfo {
76                    name: p.name,
77                    version: p.version,
78                }
79            }
80
81            // -------------------------------------------------------------
82            // SpecLocation
83            // -------------------------------------------------------------
84
85            fn loc_from(l: b::SpecLocation) -> ir::SpecLocation {
86                ir::SpecLocation {
87                    pointer: l.pointer,
88                    file: l.file,
89                }
90            }
91
92            fn loc_to(l: ir::SpecLocation) -> b::SpecLocation {
93                b::SpecLocation {
94                    pointer: l.pointer,
95                    file: l.file,
96                }
97            }
98
99            // -------------------------------------------------------------
100            // Value
101            // -------------------------------------------------------------
102
103            fn value_from(v: b::Value) -> ir::Value {
104                match v {
105                    b::Value::Null => ir::Value::Null,
106                    b::Value::Bool(value) => ir::Value::Bool { value },
107                    b::Value::Int(value) => ir::Value::Int { value },
108                    b::Value::Float(value) => ir::Value::Float { value },
109                    b::Value::String(value) => ir::Value::String { value },
110                    b::Value::List(items) => ir::Value::List { items },
111                    b::Value::Object(fields) => ir::Value::Object {
112                        fields: fields.into_iter().collect(),
113                    },
114                }
115            }
116
117            fn value_to(v: ir::Value) -> b::Value {
118                match v {
119                    ir::Value::Null => b::Value::Null,
120                    ir::Value::Bool { value } => b::Value::Bool(value),
121                    ir::Value::Int { value } => b::Value::Int(value),
122                    ir::Value::Float { value } => b::Value::Float(value),
123                    ir::Value::String { value } => b::Value::String(value),
124                    ir::Value::List { items } => b::Value::List(items),
125                    ir::Value::Object { fields } => b::Value::Object(fields),
126                }
127            }
128
129            // Extensions are `Vec<(String, ValueRef)>` — identity copy
130            // across the boundary since `ValueRef` is `u32` on both sides.
131            // Kept as functions so call sites stay readable.
132            fn extensions_from(xs: Vec<(String, ir::ValueRef)>) -> Vec<(String, ir::ValueRef)> {
133                xs
134            }
135
136            fn extensions_to(xs: Vec<(String, ir::ValueRef)>) -> Vec<(String, ir::ValueRef)> {
137                xs
138            }
139
140            // -------------------------------------------------------------
141            // Diagnostics
142            // -------------------------------------------------------------
143
144            pub fn diagnostic_from_wit(d: b::Diagnostic) -> Result<ir::Diagnostic, BindgenError> {
145                Ok(ir::Diagnostic {
146                    severity: severity_from(d.severity),
147                    code: d.code,
148                    message: d.message,
149                    location: d.location.map(loc_from),
150                    related: d.related.into_iter().map(related_from).collect(),
151                    suggested_fix: d.suggested_fix.map(fix_from),
152                })
153            }
154
155            pub fn diagnostic_to_wit(d: ir::Diagnostic) -> b::Diagnostic {
156                b::Diagnostic {
157                    severity: severity_to(d.severity),
158                    code: d.code,
159                    message: d.message,
160                    location: d.location.map(loc_to),
161                    related: d.related.into_iter().map(related_to).collect(),
162                    suggested_fix: d.suggested_fix.map(fix_to),
163                }
164            }
165
166            fn severity_from(s: b::Severity) -> ir::Severity {
167                match s {
168                    b::Severity::Error => ir::Severity::Error,
169                    b::Severity::Warning => ir::Severity::Warning,
170                    b::Severity::Info => ir::Severity::Info,
171                    b::Severity::Hint => ir::Severity::Hint,
172                }
173            }
174
175            fn severity_to(s: ir::Severity) -> b::Severity {
176                match s {
177                    ir::Severity::Error => b::Severity::Error,
178                    ir::Severity::Warning => b::Severity::Warning,
179                    ir::Severity::Info => b::Severity::Info,
180                    ir::Severity::Hint => b::Severity::Hint,
181                }
182            }
183
184            fn related_from(r: b::RelatedInfo) -> ir::RelatedInfo {
185                ir::RelatedInfo {
186                    message: r.message,
187                    location: r.location.map(loc_from),
188                }
189            }
190
191            fn related_to(r: ir::RelatedInfo) -> b::RelatedInfo {
192                b::RelatedInfo {
193                    message: r.message,
194                    location: r.location.map(loc_to),
195                }
196            }
197
198            fn fix_from(f: b::FixSuggestion) -> ir::FixSuggestion {
199                ir::FixSuggestion {
200                    message: f.message,
201                    edits: f
202                        .edits
203                        .into_iter()
204                        .map(|e| ir::FixEdit {
205                            location: loc_from(e.location),
206                            replacement: e.replacement,
207                        })
208                        .collect(),
209                }
210            }
211
212            fn fix_to(f: ir::FixSuggestion) -> b::FixSuggestion {
213                b::FixSuggestion {
214                    message: f.message,
215                    edits: f
216                        .edits
217                        .into_iter()
218                        .map(|e| b::FixEdit {
219                            location: loc_to(e.location),
220                            replacement: e.replacement,
221                        })
222                        .collect(),
223                }
224            }
225
226            // -------------------------------------------------------------
227            // StageError
228            // -------------------------------------------------------------
229
230            pub fn stage_error_from_wit(e: b_stage::StageError) -> StageErrorRepr {
231                match e {
232                    b_stage::StageError::Rejected(r) => StageErrorRepr::Rejected {
233                        reason: r.reason,
234                        // Diagnostics from a rejection are best-effort; if
235                        // any fail to convert, surface them as a plugin-bug
236                        // by collapsing to an empty list with the reason
237                        // preserved.
238                        diagnostics: r
239                            .diagnostics
240                            .into_iter()
241                            .filter_map(|d| diagnostic_from_wit(d).ok())
242                            .collect(),
243                    },
244                    b_stage::StageError::PluginBug(s) => StageErrorRepr::PluginBug(s),
245                    b_stage::StageError::ConfigInvalid(s) => StageErrorRepr::ConfigInvalid(s),
246                    b_stage::StageError::ResourceExceeded(k) => {
247                        StageErrorRepr::ResourceExceeded(match k {
248                            b_stage::ResourceKind::Fuel => ResourceKindRepr::Fuel,
249                            b_stage::ResourceKind::Memory => ResourceKindRepr::Memory,
250                            b_stage::ResourceKind::Time => ResourceKindRepr::Time,
251                            b_stage::ResourceKind::OutputSize => ResourceKindRepr::OutputSize,
252                        })
253                    }
254                }
255            }
256
257            // -------------------------------------------------------------
258            // Top-level Ir
259            // -------------------------------------------------------------
260
261            pub fn ir_to_wit(i: ir::Ir) -> b::Ir {
262                b::Ir {
263                    info: api_info_to(i.info),
264                    operations: i.operations.into_iter().map(operation_to).collect(),
265                    types: i.types.into_iter().map(named_type_to).collect(),
266                    security_schemes: i
267                        .security_schemes
268                        .into_iter()
269                        .map(security_scheme_to)
270                        .collect(),
271                    servers: i.servers.into_iter().map(server_to).collect(),
272                    webhooks: i.webhooks.into_iter().map(webhook_to).collect(),
273                    external_docs: i.external_docs.map(external_docs_to),
274                    tags: i.tags.into_iter().map(tag_to).collect(),
275                    json_schema_dialect: i.json_schema_dialect,
276                    self_url: i.self_url,
277                    values: i.values.into_iter().map(value_to).collect(),
278                }
279            }
280
281            pub fn ir_from_wit(i: b::Ir) -> Result<ir::Ir, BindgenError> {
282                let out = ir::Ir {
283                    info: api_info_from(i.info),
284                    operations: i
285                        .operations
286                        .into_iter()
287                        .map(operation_from)
288                        .collect::<Result<_, _>>()?,
289                    types: i
290                        .types
291                        .into_iter()
292                        .map(named_type_from)
293                        .collect::<Result<_, _>>()?,
294                    security_schemes: i
295                        .security_schemes
296                        .into_iter()
297                        .map(security_scheme_from)
298                        .collect(),
299                    servers: i.servers.into_iter().map(server_from).collect(),
300                    webhooks: i
301                        .webhooks
302                        .into_iter()
303                        .map(webhook_from)
304                        .collect::<Result<_, _>>()?,
305                    external_docs: i.external_docs.map(external_docs_from),
306                    tags: i.tags.into_iter().map(tag_from).collect(),
307                    json_schema_dialect: i.json_schema_dialect,
308                    self_url: i.self_url,
309                    values: i.values.into_iter().map(value_from).collect(),
310                };
311                Ok(out)
312            }
313
314            // -------------------------------------------------------------
315            // ApiInfo / Server
316            // -------------------------------------------------------------
317
318            fn api_info_from(a: b::ApiInfo) -> ir::ApiInfo {
319                ir::ApiInfo {
320                    title: a.title,
321                    version: a.version,
322                    summary: a.summary,
323                    description: a.description,
324                    terms_of_service: a.terms_of_service,
325                    contact: a.contact.map(contact_from),
326                    license_name: a.license_name,
327                    license_url: a.license_url,
328                    license_identifier: a.license_identifier,
329                    extensions: extensions_from(a.extensions),
330                }
331            }
332
333            fn api_info_to(a: ir::ApiInfo) -> b::ApiInfo {
334                b::ApiInfo {
335                    title: a.title,
336                    version: a.version,
337                    summary: a.summary,
338                    description: a.description,
339                    terms_of_service: a.terms_of_service,
340                    contact: a.contact.map(contact_to),
341                    license_name: a.license_name,
342                    license_url: a.license_url,
343                    license_identifier: a.license_identifier,
344                    extensions: extensions_to(a.extensions),
345                }
346            }
347
348            fn contact_from(c: b::Contact) -> ir::Contact {
349                ir::Contact {
350                    name: c.name,
351                    url: c.url,
352                    email: c.email,
353                }
354            }
355
356            fn contact_to(c: ir::Contact) -> b::Contact {
357                b::Contact {
358                    name: c.name,
359                    url: c.url,
360                    email: c.email,
361                }
362            }
363
364            fn server_from(s: b::Server) -> ir::Server {
365                ir::Server {
366                    url: s.url,
367                    description: s.description,
368                    name: s.name,
369                    variables: s
370                        .variables
371                        .into_iter()
372                        .map(|(k, v)| (k, server_var_from(v)))
373                        .collect(),
374                    extensions: extensions_from(s.extensions),
375                }
376            }
377
378            fn server_to(s: ir::Server) -> b::Server {
379                b::Server {
380                    url: s.url,
381                    description: s.description,
382                    name: s.name,
383                    variables: s
384                        .variables
385                        .into_iter()
386                        .map(|(k, v)| (k, server_var_to(v)))
387                        .collect(),
388                    extensions: extensions_to(s.extensions),
389                }
390            }
391
392            fn server_var_from(v: b::ServerVariable) -> ir::ServerVariable {
393                ir::ServerVariable {
394                    default: v.default,
395                    r#enum: v.enum_,
396                    description: v.description,
397                    extensions: extensions_from(v.extensions),
398                }
399            }
400
401            fn server_var_to(v: ir::ServerVariable) -> b::ServerVariable {
402                b::ServerVariable {
403                    default: v.default,
404                    enum_: v.r#enum,
405                    description: v.description,
406                    extensions: extensions_to(v.extensions),
407                }
408            }
409
410            // -------------------------------------------------------------
411            // Types
412            // -------------------------------------------------------------
413
414            fn named_type_from(n: b::NamedType) -> Result<ir::NamedType, BindgenError> {
415                Ok(ir::NamedType {
416                    id: n.id,
417                    original_name: n.original_name,
418                    title: n.title,
419                    description: n.description,
420                    deprecated: n.deprecated,
421                    read_only: n.read_only,
422                    write_only: n.write_only,
423                    external_docs: n.external_docs.map(external_docs_from),
424                    default: n.default,
425                    examples: examples_from(n.examples),
426                    xml: n.xml.map(xml_object_from),
427                    definition: type_def_from(n.definition)?,
428                    extensions: extensions_from(n.extensions),
429                    location: n.location.map(loc_from),
430                })
431            }
432
433            fn named_type_to(n: ir::NamedType) -> b::NamedType {
434                b::NamedType {
435                    id: n.id,
436                    original_name: n.original_name,
437                    title: n.title,
438                    description: n.description,
439                    deprecated: n.deprecated,
440                    read_only: n.read_only,
441                    write_only: n.write_only,
442                    external_docs: n.external_docs.map(external_docs_to),
443                    default: n.default,
444                    examples: examples_to(n.examples),
445                    xml: n.xml.map(xml_object_to),
446                    definition: type_def_to(n.definition),
447                    extensions: extensions_to(n.extensions),
448                    location: n.location.map(loc_to),
449                }
450            }
451
452            fn external_docs_from(d: b::ExternalDocs) -> ir::ExternalDocs {
453                ir::ExternalDocs {
454                    description: d.description,
455                    url: d.url,
456                }
457            }
458
459            fn external_docs_to(d: ir::ExternalDocs) -> b::ExternalDocs {
460                b::ExternalDocs {
461                    description: d.description,
462                    url: d.url,
463                }
464            }
465
466            fn example_from(e: b::Example) -> ir::Example {
467                ir::Example {
468                    summary: e.summary,
469                    description: e.description,
470                    value: e.value,
471                    external_value: e.external_value,
472                    data_value: e.data_value,
473                    serialized_value: e.serialized_value,
474                }
475            }
476
477            fn example_to(e: ir::Example) -> b::Example {
478                b::Example {
479                    summary: e.summary,
480                    description: e.description,
481                    value: e.value,
482                    external_value: e.external_value,
483                    data_value: e.data_value,
484                    serialized_value: e.serialized_value,
485                }
486            }
487
488            fn examples_from(xs: Vec<(String, b::Example)>) -> Vec<(String, ir::Example)> {
489                xs.into_iter().map(|(k, v)| (k, example_from(v))).collect()
490            }
491
492            fn examples_to(xs: Vec<(String, ir::Example)>) -> Vec<(String, b::Example)> {
493                xs.into_iter().map(|(k, v)| (k, example_to(v))).collect()
494            }
495
496            fn xml_object_from(x: b::XmlObject) -> ir::XmlObject {
497                ir::XmlObject {
498                    name: x.name,
499                    namespace: x.namespace,
500                    prefix: x.prefix,
501                    attribute: x.attribute,
502                    wrapped: x.wrapped,
503                    text: x.text,
504                    ordered: x.ordered,
505                    extensions: x.extensions,
506                }
507            }
508
509            fn xml_object_to(x: ir::XmlObject) -> b::XmlObject {
510                b::XmlObject {
511                    name: x.name,
512                    namespace: x.namespace,
513                    prefix: x.prefix,
514                    attribute: x.attribute,
515                    wrapped: x.wrapped,
516                    text: x.text,
517                    ordered: x.ordered,
518                    extensions: x.extensions,
519                }
520            }
521
522            fn link_from(l: b::Link) -> ir::Link {
523                ir::Link {
524                    operation_ref: l.operation_ref,
525                    operation_id: l.operation_id,
526                    parameters: l.parameters,
527                    request_body: l.request_body,
528                    description: l.description,
529                    server: l.server.map(server_from),
530                    extensions: l.extensions,
531                }
532            }
533
534            fn link_to(l: ir::Link) -> b::Link {
535                b::Link {
536                    operation_ref: l.operation_ref,
537                    operation_id: l.operation_id,
538                    parameters: l.parameters,
539                    request_body: l.request_body,
540                    description: l.description,
541                    server: l.server.map(server_to),
542                    extensions: l.extensions,
543                }
544            }
545
546            fn links_from(xs: Vec<(String, b::Link)>) -> Vec<(String, ir::Link)> {
547                xs.into_iter().map(|(k, v)| (k, link_from(v))).collect()
548            }
549
550            fn links_to(xs: Vec<(String, ir::Link)>) -> Vec<(String, b::Link)> {
551                xs.into_iter().map(|(k, v)| (k, link_to(v))).collect()
552            }
553
554            fn webhook_from(w: b::Webhook) -> Result<ir::Webhook, BindgenError> {
555                Ok(ir::Webhook {
556                    name: w.name,
557                    summary: w.summary,
558                    description: w.description,
559                    operations: w
560                        .operations
561                        .into_iter()
562                        .map(operation_from)
563                        .collect::<Result<_, _>>()?,
564                })
565            }
566
567            fn webhook_to(w: ir::Webhook) -> b::Webhook {
568                b::Webhook {
569                    name: w.name,
570                    summary: w.summary,
571                    description: w.description,
572                    operations: w.operations.into_iter().map(operation_to).collect(),
573                }
574            }
575
576            fn callback_from(c: b::Callback) -> ir::Callback {
577                ir::Callback {
578                    name: c.name,
579                    expression: c.expression,
580                    operation_ids: c.operation_ids,
581                    extensions: c.extensions,
582                }
583            }
584
585            fn callback_to(c: ir::Callback) -> b::Callback {
586                b::Callback {
587                    name: c.name,
588                    expression: c.expression,
589                    operation_ids: c.operation_ids,
590                    extensions: c.extensions,
591                }
592            }
593
594            fn tag_from(t: b::Tag) -> ir::Tag {
595                ir::Tag {
596                    name: t.name,
597                    summary: t.summary,
598                    description: t.description,
599                    external_docs: t.external_docs.map(external_docs_from),
600                    parent: t.parent,
601                    kind: t.kind,
602                    extensions: t.extensions,
603                }
604            }
605
606            fn tag_to(t: ir::Tag) -> b::Tag {
607                b::Tag {
608                    name: t.name,
609                    summary: t.summary,
610                    description: t.description,
611                    external_docs: t.external_docs.map(external_docs_to),
612                    parent: t.parent,
613                    kind: t.kind,
614                    extensions: t.extensions,
615                }
616            }
617
618            fn type_def_from(d: b::TypeDef) -> Result<ir::TypeDef, BindgenError> {
619                Ok(match d {
620                    b::TypeDef::Primitive(p) => ir::TypeDef::Primitive(prim_from(p)),
621                    b::TypeDef::Object(o) => ir::TypeDef::Object(object_from(o)),
622                    b::TypeDef::Array(a) => ir::TypeDef::Array(array_from(a)),
623                    b::TypeDef::EnumString(e) => ir::TypeDef::EnumString(enum_str_from(e)),
624                    b::TypeDef::EnumInt(e) => ir::TypeDef::EnumInt(enum_int_from(e)),
625                    b::TypeDef::EnumBool(e) => ir::TypeDef::EnumBool(enum_bool_from(e)),
626                    b::TypeDef::EnumNumber(e) => ir::TypeDef::EnumNumber(enum_number_from(e)),
627                    b::TypeDef::Union(u) => ir::TypeDef::Union(union_from(u)),
628                    b::TypeDef::Null => ir::TypeDef::Null,
629                    b::TypeDef::Any => ir::TypeDef::Any,
630                })
631            }
632
633            fn type_def_to(d: ir::TypeDef) -> b::TypeDef {
634                match d {
635                    ir::TypeDef::Primitive(p) => b::TypeDef::Primitive(prim_to(p)),
636                    ir::TypeDef::Object(o) => b::TypeDef::Object(object_to(o)),
637                    ir::TypeDef::Array(a) => b::TypeDef::Array(array_to(a)),
638                    ir::TypeDef::EnumString(e) => b::TypeDef::EnumString(enum_str_to(e)),
639                    ir::TypeDef::EnumInt(e) => b::TypeDef::EnumInt(enum_int_to(e)),
640                    ir::TypeDef::EnumBool(e) => b::TypeDef::EnumBool(enum_bool_to(e)),
641                    ir::TypeDef::EnumNumber(e) => b::TypeDef::EnumNumber(enum_number_to(e)),
642                    ir::TypeDef::Union(u) => b::TypeDef::Union(union_to(u)),
643                    ir::TypeDef::Null => b::TypeDef::Null,
644                    ir::TypeDef::Any => b::TypeDef::Any,
645                }
646            }
647
648            // ---- primitive ----
649
650            fn prim_kind_from(k: b::PrimitiveKind) -> ir::PrimitiveKind {
651                use b::PrimitiveKind as W;
652                use ir::PrimitiveKind as I;
653                match k {
654                    W::PrimString => I::String,
655                    W::PrimInteger => I::Integer,
656                    W::PrimNumber => I::Number,
657                    W::PrimBool => I::Bool,
658                }
659            }
660
661            fn prim_kind_to(k: ir::PrimitiveKind) -> b::PrimitiveKind {
662                use b::PrimitiveKind as W;
663                use ir::PrimitiveKind as I;
664                match k {
665                    I::String => W::PrimString,
666                    I::Integer => W::PrimInteger,
667                    I::Number => W::PrimNumber,
668                    I::Bool => W::PrimBool,
669                }
670            }
671
672            fn prim_constraints_from(c: b::PrimitiveConstraints) -> ir::PrimitiveConstraints {
673                ir::PrimitiveConstraints {
674                    minimum: c.minimum,
675                    maximum: c.maximum,
676                    exclusive_minimum: c.exclusive_minimum,
677                    exclusive_maximum: c.exclusive_maximum,
678                    multiple_of: c.multiple_of,
679                    min_length: c.min_length,
680                    max_length: c.max_length,
681                    pattern: c.pattern,
682                    format_extension: c.format_extension,
683                    content_encoding: c.content_encoding,
684                    content_media_type: c.content_media_type,
685                    content_schema: c.content_schema,
686                }
687            }
688
689            fn prim_constraints_to(c: ir::PrimitiveConstraints) -> b::PrimitiveConstraints {
690                b::PrimitiveConstraints {
691                    minimum: c.minimum,
692                    maximum: c.maximum,
693                    exclusive_minimum: c.exclusive_minimum,
694                    exclusive_maximum: c.exclusive_maximum,
695                    multiple_of: c.multiple_of,
696                    min_length: c.min_length,
697                    max_length: c.max_length,
698                    pattern: c.pattern,
699                    format_extension: c.format_extension,
700                    content_encoding: c.content_encoding,
701                    content_media_type: c.content_media_type,
702                    content_schema: c.content_schema,
703                }
704            }
705
706            fn prim_from(p: b::PrimitiveType) -> ir::PrimitiveType {
707                ir::PrimitiveType {
708                    kind: prim_kind_from(p.kind),
709                    constraints: prim_constraints_from(p.constraints),
710                }
711            }
712
713            fn prim_to(p: ir::PrimitiveType) -> b::PrimitiveType {
714                b::PrimitiveType {
715                    kind: prim_kind_to(p.kind),
716                    constraints: prim_constraints_to(p.constraints),
717                }
718            }
719
720            // ---- array ----
721
722            fn array_from(a: b::ArrayType) -> ir::ArrayType {
723                ir::ArrayType {
724                    items: a.items,
725                    constraints: ir::ArrayConstraints {
726                        min_items: a.constraints.min_items,
727                        max_items: a.constraints.max_items,
728                        unique_items: a.constraints.unique_items,
729                    },
730                }
731            }
732
733            fn array_to(a: ir::ArrayType) -> b::ArrayType {
734                b::ArrayType {
735                    items: a.items,
736                    constraints: b::ArrayConstraints {
737                        min_items: a.constraints.min_items,
738                        max_items: a.constraints.max_items,
739                        unique_items: a.constraints.unique_items,
740                    },
741                }
742            }
743
744            // ---- object ----
745
746            fn object_from(o: b::ObjectType) -> ir::ObjectType {
747                ir::ObjectType {
748                    properties: o
749                        .properties
750                        .into_iter()
751                        .map(|p| ir::Property {
752                            name: p.name,
753                            r#type: p.type_,
754                            required: p.required,
755                            title: p.title,
756                            description: p.description,
757                            deprecated: p.deprecated,
758                            read_only: p.read_only,
759                            write_only: p.write_only,
760                            external_docs: p.external_docs.map(external_docs_from),
761                            default: p.default,
762                            examples: examples_from(p.examples),
763                            extensions: p.extensions,
764                        })
765                        .collect(),
766                    pattern_properties: o
767                        .pattern_properties
768                        .into_iter()
769                        .map(|p| ir::PatternProperty {
770                            pattern: p.pattern,
771                            r#type: p.type_,
772                        })
773                        .collect(),
774                    additional_properties: match o.additional_properties {
775                        b::AdditionalProperties::Forbidden => ir::AdditionalProperties::Forbidden,
776                        b::AdditionalProperties::Any => ir::AdditionalProperties::Any,
777                        b::AdditionalProperties::Typed(t) => {
778                            ir::AdditionalProperties::Typed { r#type: t }
779                        }
780                    },
781                    property_names: o.property_names,
782                    constraints: ir::ObjectConstraints {
783                        min_properties: o.constraints.min_properties,
784                        max_properties: o.constraints.max_properties,
785                    },
786                }
787            }
788
789            fn object_to(o: ir::ObjectType) -> b::ObjectType {
790                b::ObjectType {
791                    properties: o
792                        .properties
793                        .into_iter()
794                        .map(|p| b::Property {
795                            name: p.name,
796                            type_: p.r#type,
797                            required: p.required,
798                            title: p.title,
799                            description: p.description,
800                            deprecated: p.deprecated,
801                            read_only: p.read_only,
802                            write_only: p.write_only,
803                            external_docs: p.external_docs.map(external_docs_to),
804                            default: p.default,
805                            examples: examples_to(p.examples),
806                            extensions: p.extensions,
807                        })
808                        .collect(),
809                    pattern_properties: o
810                        .pattern_properties
811                        .into_iter()
812                        .map(|p| b::PatternProperty {
813                            pattern: p.pattern,
814                            type_: p.r#type,
815                        })
816                        .collect(),
817                    additional_properties: match o.additional_properties {
818                        ir::AdditionalProperties::Forbidden => b::AdditionalProperties::Forbidden,
819                        ir::AdditionalProperties::Any => b::AdditionalProperties::Any,
820                        ir::AdditionalProperties::Typed { r#type } => {
821                            b::AdditionalProperties::Typed(r#type)
822                        }
823                    },
824                    property_names: o.property_names,
825                    constraints: b::ObjectConstraints {
826                        min_properties: o.constraints.min_properties,
827                        max_properties: o.constraints.max_properties,
828                    },
829                }
830            }
831
832            // ---- map ----
833
834            // ---- enums ----
835
836            fn enum_str_from(e: b::EnumStringType) -> ir::EnumStringType {
837                ir::EnumStringType {
838                    values: e
839                        .values
840                        .into_iter()
841                        .map(|v| ir::EnumStringValue { value: v.value })
842                        .collect(),
843                }
844            }
845
846            fn enum_str_to(e: ir::EnumStringType) -> b::EnumStringType {
847                b::EnumStringType {
848                    values: e
849                        .values
850                        .into_iter()
851                        .map(|v| b::EnumStringValue { value: v.value })
852                        .collect(),
853                }
854            }
855
856            fn enum_int_from(e: b::EnumIntType) -> ir::EnumIntType {
857                ir::EnumIntType {
858                    values: e
859                        .values
860                        .into_iter()
861                        .map(|v| ir::EnumIntValue { value: v.value })
862                        .collect(),
863                    kind: match e.kind {
864                        b::IntKind::Int32 => ir::IntKind::Int32,
865                        b::IntKind::Int64 => ir::IntKind::Int64,
866                    },
867                }
868            }
869
870            fn enum_int_to(e: ir::EnumIntType) -> b::EnumIntType {
871                b::EnumIntType {
872                    values: e
873                        .values
874                        .into_iter()
875                        .map(|v| b::EnumIntValue { value: v.value })
876                        .collect(),
877                    kind: match e.kind {
878                        ir::IntKind::Int32 => b::IntKind::Int32,
879                        ir::IntKind::Int64 => b::IntKind::Int64,
880                    },
881                }
882            }
883
884            fn enum_bool_from(e: b::EnumBoolType) -> ir::EnumBoolType {
885                ir::EnumBoolType {
886                    values: e
887                        .values
888                        .into_iter()
889                        .map(|v| ir::EnumBoolValue { value: v.value })
890                        .collect(),
891                }
892            }
893
894            fn enum_bool_to(e: ir::EnumBoolType) -> b::EnumBoolType {
895                b::EnumBoolType {
896                    values: e
897                        .values
898                        .into_iter()
899                        .map(|v| b::EnumBoolValue { value: v.value })
900                        .collect(),
901                }
902            }
903
904            fn enum_number_from(e: b::EnumNumberType) -> ir::EnumNumberType {
905                ir::EnumNumberType {
906                    values: e
907                        .values
908                        .into_iter()
909                        .map(|v| ir::EnumNumberValue { value: v.value })
910                        .collect(),
911                    kind: match e.kind {
912                        b::NumberKind::Float => ir::NumberKind::Float,
913                        b::NumberKind::Double => ir::NumberKind::Double,
914                    },
915                }
916            }
917
918            fn enum_number_to(e: ir::EnumNumberType) -> b::EnumNumberType {
919                b::EnumNumberType {
920                    values: e
921                        .values
922                        .into_iter()
923                        .map(|v| b::EnumNumberValue { value: v.value })
924                        .collect(),
925                    kind: match e.kind {
926                        ir::NumberKind::Float => b::NumberKind::Float,
927                        ir::NumberKind::Double => b::NumberKind::Double,
928                    },
929                }
930            }
931
932            // ---- union ----
933
934            fn union_from(u: b::UnionType) -> ir::UnionType {
935                ir::UnionType {
936                    variants: u
937                        .variants
938                        .into_iter()
939                        .map(|v| ir::UnionVariant {
940                            r#type: v.type_,
941                            tag: v.tag,
942                        })
943                        .collect(),
944                    discriminator: u.discriminator.map(|d| ir::Discriminator {
945                        property_name: d.property_name,
946                        mapping: d.mapping.into_iter().collect(),
947                        extensions: d.extensions,
948                    }),
949                    kind: match u.kind {
950                        b::UnionKind::OneOf => ir::UnionKind::OneOf,
951                        b::UnionKind::AnyOf => ir::UnionKind::AnyOf,
952                    },
953                }
954            }
955
956            fn union_to(u: ir::UnionType) -> b::UnionType {
957                b::UnionType {
958                    variants: u
959                        .variants
960                        .into_iter()
961                        .map(|v| b::UnionVariant {
962                            type_: v.r#type,
963                            tag: v.tag,
964                        })
965                        .collect(),
966                    discriminator: u.discriminator.map(|d| b::Discriminator {
967                        property_name: d.property_name,
968                        mapping: d.mapping,
969                        extensions: d.extensions,
970                    }),
971                    kind: match u.kind {
972                        ir::UnionKind::OneOf => b::UnionKind::OneOf,
973                        ir::UnionKind::AnyOf => b::UnionKind::AnyOf,
974                    },
975                }
976            }
977
978            // -------------------------------------------------------------
979            // Operations
980            // -------------------------------------------------------------
981
982            fn http_method_from(m: b::HttpMethod) -> ir::HttpMethod {
983                use b::HttpMethod as W;
984                use ir::HttpMethod as I;
985                match m {
986                    W::Get => I::Get,
987                    W::Put => I::Put,
988                    W::Post => I::Post,
989                    W::Delete => I::Delete,
990                    W::Options => I::Options,
991                    W::Head => I::Head,
992                    W::Patch => I::Patch,
993                    W::Trace => I::Trace,
994                    W::Other(s) => I::Other(s),
995                }
996            }
997
998            fn http_method_to(m: ir::HttpMethod) -> b::HttpMethod {
999                use b::HttpMethod as W;
1000                use ir::HttpMethod as I;
1001                match m {
1002                    I::Get => W::Get,
1003                    I::Put => W::Put,
1004                    I::Post => W::Post,
1005                    I::Delete => W::Delete,
1006                    I::Options => W::Options,
1007                    I::Head => W::Head,
1008                    I::Patch => W::Patch,
1009                    I::Trace => W::Trace,
1010                    I::Other(s) => W::Other(s),
1011                }
1012            }
1013
1014            fn param_style_from(s: b::ParameterStyle) -> ir::ParameterStyle {
1015                use b::ParameterStyle as W;
1016                use ir::ParameterStyle as I;
1017                match s {
1018                    W::ParamForm => I::Form,
1019                    W::ParamSimple => I::Simple,
1020                    W::ParamLabel => I::Label,
1021                    W::ParamMatrix => I::Matrix,
1022                    W::ParamSpaceDelimited => I::SpaceDelimited,
1023                    W::ParamPipeDelimited => I::PipeDelimited,
1024                    W::ParamDeepObject => I::DeepObject,
1025                }
1026            }
1027
1028            fn param_style_to(s: ir::ParameterStyle) -> b::ParameterStyle {
1029                use b::ParameterStyle as W;
1030                use ir::ParameterStyle as I;
1031                match s {
1032                    I::Form => W::ParamForm,
1033                    I::Simple => W::ParamSimple,
1034                    I::Label => W::ParamLabel,
1035                    I::Matrix => W::ParamMatrix,
1036                    I::SpaceDelimited => W::ParamSpaceDelimited,
1037                    I::PipeDelimited => W::ParamPipeDelimited,
1038                    I::DeepObject => W::ParamDeepObject,
1039                }
1040            }
1041
1042            fn header_from(h: b::Header) -> ir::Header {
1043                ir::Header {
1044                    r#type: h.type_,
1045                    required: h.required,
1046                    description: h.description,
1047                    deprecated: h.deprecated,
1048                    examples: examples_from(h.examples),
1049                    style: h.style.map(param_style_from),
1050                    explode: h.explode,
1051                    allow_reserved: h.allow_reserved,
1052                    allow_empty_value: h.allow_empty_value,
1053                    location: h.location.map(loc_from),
1054                }
1055            }
1056
1057            fn header_to(h: ir::Header) -> b::Header {
1058                b::Header {
1059                    type_: h.r#type,
1060                    required: h.required,
1061                    description: h.description,
1062                    deprecated: h.deprecated,
1063                    examples: examples_to(h.examples),
1064                    style: h.style.map(param_style_to),
1065                    explode: h.explode,
1066                    allow_reserved: h.allow_reserved,
1067                    allow_empty_value: h.allow_empty_value,
1068                    location: h.location.map(loc_to),
1069                }
1070            }
1071
1072            fn parameter_from(p: b::Parameter) -> ir::Parameter {
1073                ir::Parameter {
1074                    name: p.name,
1075                    r#type: p.type_,
1076                    required: p.required,
1077                    description: p.description,
1078                    deprecated: p.deprecated,
1079                    examples: examples_from(p.examples),
1080                    style: p.style.map(param_style_from),
1081                    explode: p.explode,
1082                    allow_empty_value: p.allow_empty_value,
1083                    allow_reserved: p.allow_reserved,
1084                    extensions: extensions_from(p.extensions),
1085                    location: p.location.map(loc_from),
1086                }
1087            }
1088
1089            fn parameter_to(p: ir::Parameter) -> b::Parameter {
1090                b::Parameter {
1091                    name: p.name,
1092                    type_: p.r#type,
1093                    required: p.required,
1094                    description: p.description,
1095                    deprecated: p.deprecated,
1096                    examples: examples_to(p.examples),
1097                    style: p.style.map(param_style_to),
1098                    explode: p.explode,
1099                    allow_empty_value: p.allow_empty_value,
1100                    allow_reserved: p.allow_reserved,
1101                    extensions: extensions_to(p.extensions),
1102                    location: p.location.map(loc_to),
1103                }
1104            }
1105
1106            fn body_from(b_: b::Body) -> ir::Body {
1107                ir::Body {
1108                    content: b_.content.into_iter().map(body_content_from).collect(),
1109                    required: b_.required,
1110                    description: b_.description,
1111                    extensions: extensions_from(b_.extensions),
1112                }
1113            }
1114
1115            fn body_to(b_: ir::Body) -> b::Body {
1116                b::Body {
1117                    content: b_.content.into_iter().map(body_content_to).collect(),
1118                    required: b_.required,
1119                    description: b_.description,
1120                    extensions: extensions_to(b_.extensions),
1121                }
1122            }
1123
1124            fn body_content_from(c: b::BodyContent) -> ir::BodyContent {
1125                ir::BodyContent {
1126                    media_type: c.media_type,
1127                    r#type: c.type_,
1128                    encoding: c
1129                        .encoding
1130                        .into_iter()
1131                        .map(|(k, v)| (k, encoding_from(v)))
1132                        .collect(),
1133                    item_schema: c.item_schema,
1134                    examples: examples_from(c.examples),
1135                    extensions: extensions_from(c.extensions),
1136                }
1137            }
1138
1139            fn body_content_to(c: ir::BodyContent) -> b::BodyContent {
1140                b::BodyContent {
1141                    media_type: c.media_type,
1142                    type_: c.r#type,
1143                    encoding: c
1144                        .encoding
1145                        .into_iter()
1146                        .map(|(k, v)| (k, encoding_to(v)))
1147                        .collect(),
1148                    item_schema: c.item_schema,
1149                    examples: examples_to(c.examples),
1150                    extensions: extensions_to(c.extensions),
1151                }
1152            }
1153
1154            fn encoding_from(e: b::Encoding) -> ir::Encoding {
1155                ir::Encoding {
1156                    content_type: e.content_type,
1157                    style: e.style.map(param_style_from),
1158                    explode: e.explode,
1159                    allow_reserved: e.allow_reserved,
1160                    headers: e
1161                        .headers
1162                        .into_iter()
1163                        .map(|(k, v)| (k, header_from(v)))
1164                        .collect(),
1165                    extensions: extensions_from(e.extensions),
1166                }
1167            }
1168
1169            fn encoding_to(e: ir::Encoding) -> b::Encoding {
1170                b::Encoding {
1171                    content_type: e.content_type,
1172                    style: e.style.map(param_style_to),
1173                    explode: e.explode,
1174                    allow_reserved: e.allow_reserved,
1175                    headers: e
1176                        .headers
1177                        .into_iter()
1178                        .map(|(k, v)| (k, header_to(v)))
1179                        .collect(),
1180                    extensions: extensions_to(e.extensions),
1181                }
1182            }
1183
1184            fn response_from(r: b::Response) -> ir::Response {
1185                ir::Response {
1186                    status: match r.status {
1187                        b::ResponseStatus::Explicit(code) => ir::ResponseStatus::Explicit { code },
1188                        b::ResponseStatus::Default => ir::ResponseStatus::Default,
1189                        b::ResponseStatus::Range(class) => ir::ResponseStatus::Range { class },
1190                    },
1191                    content: r.content.into_iter().map(body_content_from).collect(),
1192                    headers: r
1193                        .headers
1194                        .into_iter()
1195                        .map(|(k, v)| (k, header_from(v)))
1196                        .collect(),
1197                    summary: r.summary,
1198                    description: r.description,
1199                    links: links_from(r.links),
1200                    extensions: extensions_from(r.extensions),
1201                }
1202            }
1203
1204            fn response_to(r: ir::Response) -> b::Response {
1205                b::Response {
1206                    status: match r.status {
1207                        ir::ResponseStatus::Explicit { code } => b::ResponseStatus::Explicit(code),
1208                        ir::ResponseStatus::Default => b::ResponseStatus::Default,
1209                        ir::ResponseStatus::Range { class } => b::ResponseStatus::Range(class),
1210                    },
1211                    content: r.content.into_iter().map(body_content_to).collect(),
1212                    headers: r
1213                        .headers
1214                        .into_iter()
1215                        .map(|(k, v)| (k, header_to(v)))
1216                        .collect(),
1217                    summary: r.summary,
1218                    description: r.description,
1219                    links: links_to(r.links),
1220                    extensions: extensions_to(r.extensions),
1221                }
1222            }
1223
1224            fn operation_from(op: b::Operation) -> Result<ir::Operation, BindgenError> {
1225                Ok(ir::Operation {
1226                    id: op.id,
1227                    original_id: op.original_id,
1228                    method: http_method_from(op.method),
1229                    path_template: op.path_template,
1230                    path_params: op.path_params.into_iter().map(parameter_from).collect(),
1231                    query_params: op.query_params.into_iter().map(parameter_from).collect(),
1232                    header_params: op.header_params.into_iter().map(parameter_from).collect(),
1233                    cookie_params: op.cookie_params.into_iter().map(parameter_from).collect(),
1234                    querystring_params: op
1235                        .querystring_params
1236                        .into_iter()
1237                        .map(parameter_from)
1238                        .collect(),
1239                    request_body: op.request_body.map(body_from),
1240                    responses: op.responses.into_iter().map(response_from).collect(),
1241                    security: op
1242                        .security
1243                        .into_iter()
1244                        .map(|s| ir::SecurityRequirement {
1245                            scheme_id: s.scheme_id,
1246                            scopes: s.scopes,
1247                        })
1248                        .collect(),
1249                    tags: op.tags,
1250                    summary: op.summary,
1251                    description: op.description,
1252                    deprecated: op.deprecated,
1253                    external_docs: op.external_docs.map(external_docs_from),
1254                    extensions: op.extensions,
1255                    servers: op.servers.into_iter().map(server_from).collect(),
1256                    callbacks: op.callbacks.into_iter().map(callback_from).collect(),
1257                    location: op.location.map(loc_from),
1258                })
1259            }
1260
1261            fn operation_to(op: ir::Operation) -> b::Operation {
1262                b::Operation {
1263                    id: op.id,
1264                    original_id: op.original_id,
1265                    method: http_method_to(op.method),
1266                    path_template: op.path_template,
1267                    path_params: op.path_params.into_iter().map(parameter_to).collect(),
1268                    query_params: op.query_params.into_iter().map(parameter_to).collect(),
1269                    header_params: op.header_params.into_iter().map(parameter_to).collect(),
1270                    cookie_params: op.cookie_params.into_iter().map(parameter_to).collect(),
1271                    querystring_params: op
1272                        .querystring_params
1273                        .into_iter()
1274                        .map(parameter_to)
1275                        .collect(),
1276                    request_body: op.request_body.map(body_to),
1277                    responses: op.responses.into_iter().map(response_to).collect(),
1278                    security: op
1279                        .security
1280                        .into_iter()
1281                        .map(|s| b::SecurityRequirement {
1282                            scheme_id: s.scheme_id,
1283                            scopes: s.scopes,
1284                        })
1285                        .collect(),
1286                    tags: op.tags,
1287                    summary: op.summary,
1288                    description: op.description,
1289                    deprecated: op.deprecated,
1290                    external_docs: op.external_docs.map(external_docs_to),
1291                    extensions: op.extensions,
1292                    servers: op.servers.into_iter().map(server_to).collect(),
1293                    callbacks: op.callbacks.into_iter().map(callback_to).collect(),
1294                    location: op.location.map(loc_to),
1295                }
1296            }
1297
1298            // -------------------------------------------------------------
1299            // Security
1300            // -------------------------------------------------------------
1301
1302            fn security_scheme_from(s: b::SecurityScheme) -> ir::SecurityScheme {
1303                ir::SecurityScheme {
1304                    id: s.id,
1305                    kind: match s.kind {
1306                        b::SecuritySchemeKind::ApiKey(k) => {
1307                            ir::SecuritySchemeKind::ApiKey(ir::ApiKeyScheme {
1308                                name: k.name,
1309                                location: match k.location {
1310                                    b::ApiKeyLocation::Header => ir::ApiKeyLocation::Header,
1311                                    b::ApiKeyLocation::Query => ir::ApiKeyLocation::Query,
1312                                    b::ApiKeyLocation::Cookie => ir::ApiKeyLocation::Cookie,
1313                                },
1314                            })
1315                        }
1316                        b::SecuritySchemeKind::HttpBasic => ir::SecuritySchemeKind::HttpBasic,
1317                        b::SecuritySchemeKind::HttpBearer(f) => {
1318                            ir::SecuritySchemeKind::HttpBearer { bearer_format: f }
1319                        }
1320                        b::SecuritySchemeKind::MutualTls => ir::SecuritySchemeKind::MutualTls,
1321                        b::SecuritySchemeKind::Oauth2(o) => {
1322                            ir::SecuritySchemeKind::Oauth2(ir::OAuth2Scheme {
1323                                flows: o.flows.into_iter().map(oauth2_flow_from).collect(),
1324                            })
1325                        }
1326                        b::SecuritySchemeKind::OpenIdConnect(u) => {
1327                            ir::SecuritySchemeKind::OpenIdConnect { url: u }
1328                        }
1329                    },
1330                    description: s.description,
1331                    deprecated: s.deprecated,
1332                    extensions: extensions_from(s.extensions),
1333                }
1334            }
1335
1336            fn security_scheme_to(s: ir::SecurityScheme) -> b::SecurityScheme {
1337                b::SecurityScheme {
1338                    id: s.id,
1339                    kind: match s.kind {
1340                        ir::SecuritySchemeKind::ApiKey(k) => {
1341                            b::SecuritySchemeKind::ApiKey(b::ApiKeyScheme {
1342                                name: k.name,
1343                                location: match k.location {
1344                                    ir::ApiKeyLocation::Header => b::ApiKeyLocation::Header,
1345                                    ir::ApiKeyLocation::Query => b::ApiKeyLocation::Query,
1346                                    ir::ApiKeyLocation::Cookie => b::ApiKeyLocation::Cookie,
1347                                },
1348                            })
1349                        }
1350                        ir::SecuritySchemeKind::HttpBasic => b::SecuritySchemeKind::HttpBasic,
1351                        ir::SecuritySchemeKind::HttpBearer { bearer_format } => {
1352                            b::SecuritySchemeKind::HttpBearer(bearer_format)
1353                        }
1354                        ir::SecuritySchemeKind::MutualTls => b::SecuritySchemeKind::MutualTls,
1355                        ir::SecuritySchemeKind::Oauth2(o) => {
1356                            b::SecuritySchemeKind::Oauth2(b::Oauth2Scheme {
1357                                flows: o.flows.into_iter().map(oauth2_flow_to).collect(),
1358                            })
1359                        }
1360                        ir::SecuritySchemeKind::OpenIdConnect { url } => {
1361                            b::SecuritySchemeKind::OpenIdConnect(url)
1362                        }
1363                    },
1364                    description: s.description,
1365                    deprecated: s.deprecated,
1366                    extensions: extensions_to(s.extensions),
1367                }
1368            }
1369
1370            fn oauth2_flow_from(f: b::Oauth2Flow) -> ir::OAuth2Flow {
1371                ir::OAuth2Flow {
1372                    kind: match f.kind {
1373                        b::Oauth2FlowKind::Implicit => ir::OAuth2FlowKind::Implicit,
1374                        b::Oauth2FlowKind::Password => ir::OAuth2FlowKind::Password,
1375                        b::Oauth2FlowKind::ClientCredentials => {
1376                            ir::OAuth2FlowKind::ClientCredentials
1377                        }
1378                        b::Oauth2FlowKind::AuthorizationCode => {
1379                            ir::OAuth2FlowKind::AuthorizationCode
1380                        }
1381                    },
1382                    authorization_url: f.authorization_url,
1383                    token_url: f.token_url,
1384                    refresh_url: f.refresh_url,
1385                    scopes: f.scopes,
1386                    extensions: extensions_from(f.extensions),
1387                }
1388            }
1389
1390            fn oauth2_flow_to(f: ir::OAuth2Flow) -> b::Oauth2Flow {
1391                b::Oauth2Flow {
1392                    kind: match f.kind {
1393                        ir::OAuth2FlowKind::Implicit => b::Oauth2FlowKind::Implicit,
1394                        ir::OAuth2FlowKind::Password => b::Oauth2FlowKind::Password,
1395                        ir::OAuth2FlowKind::ClientCredentials => {
1396                            b::Oauth2FlowKind::ClientCredentials
1397                        }
1398                        ir::OAuth2FlowKind::AuthorizationCode => {
1399                            b::Oauth2FlowKind::AuthorizationCode
1400                        }
1401                    },
1402                    authorization_url: f.authorization_url,
1403                    token_url: f.token_url,
1404                    refresh_url: f.refresh_url,
1405                    scopes: f.scopes,
1406                    extensions: extensions_to(f.extensions),
1407                }
1408            }
1409        }
1410    };
1411}
1412
1413define_world_conversions!(transformer, transformer);
1414define_world_conversions!(generator, generator);
1415
1416#[cfg(test)]
1417mod tests {
1418    use super::*;
1419    use forge_ir::proptest_util::small_ir;
1420    use proptest::prelude::*;
1421
1422    proptest! {
1423        /// Roundtrip: forge_ir → wit (transformer) → forge_ir is identity.
1424        #[test]
1425        fn roundtrip_transformer(ir in small_ir()) {
1426            let wit = transformer::ir_to_wit(ir.clone());
1427            let back = transformer::ir_from_wit(wit).unwrap();
1428            prop_assert_eq!(ir, back);
1429        }
1430
1431        /// Roundtrip: forge_ir → wit (generator) → forge_ir is identity.
1432        #[test]
1433        fn roundtrip_generator(ir in small_ir()) {
1434            let wit = generator::ir_to_wit(ir.clone());
1435            let back = generator::ir_from_wit(wit).unwrap();
1436            prop_assert_eq!(ir, back);
1437        }
1438    }
1439}