Skip to main content

typify_impl/
type_entry.rs

1// Copyright 2025 Oxide Computer Company
2
3use std::collections::{BTreeMap, BTreeSet, HashMap};
4
5use proc_macro2::{Punct, Spacing, TokenStream, TokenTree};
6use quote::{format_ident, quote, ToTokens};
7use schemars::schema::{Metadata, Schema};
8use syn::Path;
9use unicode_ident::is_xid_continue;
10
11use crate::{
12    enums::output_variant,
13    output::{OutputSpace, OutputSpaceMod},
14    sanitize,
15    structs::{generate_serde_attr, DefaultFunction},
16    util::{get_type_name, metadata_description, unique, TypePatch},
17    Case, DefaultImpl, Name, Result, TypeId, TypeSpace, TypeSpaceImpl,
18};
19
20#[derive(Debug, Clone, PartialEq)]
21pub(crate) struct SchemaWrapper(Schema);
22
23impl Eq for SchemaWrapper {}
24
25impl Ord for SchemaWrapper {
26    fn cmp(&self, _other: &Self) -> std::cmp::Ordering {
27        std::cmp::Ordering::Equal
28    }
29}
30impl PartialOrd for SchemaWrapper {
31    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
32        Some(self.cmp(other))
33    }
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
37pub(crate) struct TypeEntryEnum {
38    pub name: String,
39    pub rename: Option<String>,
40    pub description: Option<String>,
41    pub default: Option<WrappedValue>,
42    pub tag_type: EnumTagType,
43    pub variants: Vec<Variant>,
44    pub deny_unknown_fields: bool,
45    pub bespoke_impls: BTreeSet<TypeEntryEnumImpl>,
46    pub schema: SchemaWrapper,
47}
48
49/// Cached attributes that (mostly) result in customized impl generation.
50#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
51pub(crate) enum TypeEntryEnumImpl {
52    AllSimpleVariants,
53    UntaggedFromStr,
54    UntaggedDisplay,
55    /// This is a cached marker to let us know that at least one of the
56    /// variants is irrefutably a string. There is currently no associated
57    /// implementation that we generate.
58    UntaggedFromStringIrrefutable,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
62pub(crate) struct TypeEntryStruct {
63    pub name: String,
64    pub rename: Option<String>,
65    pub description: Option<String>,
66    pub default: Option<WrappedValue>,
67    pub properties: Vec<StructProperty>,
68    pub deny_unknown_fields: bool,
69    pub schema: SchemaWrapper,
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
73pub(crate) struct TypeEntryNewtype {
74    pub name: String,
75    pub rename: Option<String>,
76    pub description: Option<String>,
77    pub default: Option<WrappedValue>,
78    pub type_id: TypeId,
79    pub constraints: TypeEntryNewtypeConstraints,
80    pub schema: SchemaWrapper,
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
84pub(crate) enum TypeEntryNewtypeConstraints {
85    None,
86    EnumValue(Vec<WrappedValue>),
87    DenyValue(Vec<WrappedValue>),
88    String {
89        max_length: Option<u32>,
90        min_length: Option<u32>,
91        pattern: Option<String>,
92    },
93}
94
95#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
96pub(crate) struct TypeEntryNative {
97    pub type_name: String,
98    impls: Vec<TypeSpaceImpl>,
99    // TODO to support const generics, this can be some sort of TypeOrValue,
100    // but note that we may some day need to disambiguate char and &'static str
101    // since schemars represents a char as a string of length 1.
102    pub parameters: Vec<TypeId>,
103}
104impl TypeEntryNative {
105    pub(crate) fn name_match(&self, type_name: &Name) -> bool {
106        let native_name = self.type_name.rsplit("::").next().unwrap();
107        !self.parameters.is_empty()
108            || matches!(type_name, Name::Required(req) if req == native_name)
109    }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
113pub(crate) struct WrappedValue(pub serde_json::Value);
114impl WrappedValue {
115    pub(crate) fn new(value: serde_json::Value) -> Self {
116        Self(value)
117    }
118}
119
120impl Ord for WrappedValue {
121    fn cmp(&self, _: &Self) -> std::cmp::Ordering {
122        std::cmp::Ordering::Equal
123    }
124}
125impl PartialOrd for WrappedValue {
126    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
127        Some(self.cmp(other))
128    }
129}
130
131// TODO This struct needs to go away (again). The derives should go into the
132// generated struct/enum/newtype structs. Same for the impls. Native types will
133// also have impls. Builtin generic types such as Box or Vec will delegate to
134// their subtypes (while recursive, it is necessarily terminating... though I
135// suppose we could memoize it). Builtin simple types such as u64 or String
136// have a static list.
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub(crate) struct TypeEntry {
139    pub details: TypeEntryDetails,
140    pub extra_derives: BTreeSet<String>,
141    pub extra_attrs: BTreeSet<String>,
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
145pub(crate) enum TypeEntryDetails {
146    Enum(TypeEntryEnum),
147    Struct(TypeEntryStruct),
148    Newtype(TypeEntryNewtype),
149
150    /// Native types exported from a well-known crate.
151    Native(TypeEntryNative),
152
153    // Types from core and std.
154    Option(TypeId),
155    Box(TypeId),
156    Vec(TypeId),
157    Map(TypeId, TypeId),
158    Set(TypeId),
159    Array(TypeId, usize),
160    Tuple(Vec<TypeId>),
161    Unit,
162    Boolean,
163    /// Integers
164    Integer(String),
165    /// Floating point numbers; not Eq, Ord, or Hash
166    Float(String),
167    /// Strings... which we handle a little specially.
168    String,
169    /// serde_json::Value which we also handle specially.
170    JsonValue,
171
172    /// While these types won't very make their way out to the user, we need
173    /// reference types in particular to represent simple type aliases between
174    /// types named as reference targets.
175    Reference(TypeId),
176}
177
178#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
179pub(crate) enum EnumTagType {
180    External,
181    Internal { tag: String },
182    Adjacent { tag: String, content: String },
183    Untagged,
184}
185
186#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
187pub(crate) struct Variant {
188    pub raw_name: String,
189    pub ident_name: Option<String>,
190    pub description: Option<String>,
191    pub details: VariantDetails,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
195pub(crate) enum VariantDetails {
196    Simple,
197    Item(TypeId),
198    Tuple(Vec<TypeId>),
199    Struct(Vec<StructProperty>),
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
203pub(crate) struct StructProperty {
204    pub name: String,
205    pub rename: StructPropertyRename,
206    pub state: StructPropertyState,
207    pub description: Option<String>,
208    pub type_id: TypeId,
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
212pub(crate) enum StructPropertyRename {
213    None,
214    Rename(String),
215    Flatten,
216}
217
218#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
219pub(crate) enum StructPropertyState {
220    Required,
221    Optional,
222    Default(WrappedValue),
223}
224
225#[derive(Debug)]
226pub(crate) enum DefaultKind {
227    Intrinsic,
228    Specific,
229    Generic(DefaultImpl),
230}
231
232fn variants_unique(variants: &[Variant]) -> bool {
233    unique(
234        variants
235            .iter()
236            .map(|variant| variant.ident_name.as_ref().unwrap()),
237    )
238}
239
240impl TypeEntryEnum {
241    pub(crate) fn from_metadata(
242        type_space: &TypeSpace,
243        type_name: Name,
244        metadata: &Option<Box<Metadata>>,
245        tag_type: EnumTagType,
246        mut variants: Vec<Variant>,
247        deny_unknown_fields: bool,
248        schema: Schema,
249    ) -> TypeEntry {
250        // Let's find some decent names for variants. We first try the simple
251        // sanitization.
252        variants.iter_mut().for_each(|variant| {
253            let ident_name = sanitize(&variant.raw_name, Case::Pascal);
254            variant.ident_name = Some(ident_name);
255        });
256
257        // If variants aren't unique, we're turn the elided characters into
258        // 'x's.
259        if !variants_unique(&variants) {
260            variants.iter_mut().for_each(|variant| {
261                let ident_name = sanitize(
262                    &variant
263                        .raw_name
264                        .replace(|c| c == '_' || !is_xid_continue(c), "X"),
265                    Case::Pascal,
266                );
267                variant.ident_name = Some(ident_name);
268            });
269        }
270
271        // If variants still aren't unique, we fail: we'd rather not emit code
272        // that can't compile
273        if !variants_unique(&variants) {
274            let mut counts = HashMap::new();
275            variants.iter().for_each(|variant| {
276                counts
277                    .entry(variant.ident_name.as_ref().unwrap())
278                    .and_modify(|xxx| *xxx += 1)
279                    .or_insert(0);
280            });
281            let dups = variants
282                .iter()
283                .filter(|variant| *counts.get(variant.ident_name.as_ref().unwrap()).unwrap() > 0)
284                .map(|variant| variant.raw_name.as_str())
285                .collect::<Vec<_>>()
286                .join(",");
287            panic!("Failed to make unique variant names for [{}]", dups);
288        }
289
290        let name = get_type_name(&type_name, metadata).unwrap();
291        let rename = None;
292        let description = metadata_description(metadata);
293
294        let type_patch = TypePatch::new(type_space, name);
295
296        let details = TypeEntryDetails::Enum(Self {
297            name: type_patch.name,
298            rename,
299            description,
300            default: None,
301            tag_type,
302            variants,
303            deny_unknown_fields,
304            bespoke_impls: Default::default(),
305            schema: SchemaWrapper(schema),
306        });
307
308        TypeEntry {
309            details,
310            extra_derives: type_patch.derives,
311            extra_attrs: type_patch.attrs,
312        }
313    }
314
315    pub(crate) fn finalize(&mut self, type_space: &TypeSpace) {
316        self.bespoke_impls = [
317            // Not untagged with all simple variants.
318            (self.tag_type != EnumTagType::Untagged
319                && !self.variants.is_empty()
320                && self
321                    .variants
322                    .iter()
323                    .all(|variant| matches!(variant.details, VariantDetails::Simple)))
324            .then_some(TypeEntryEnumImpl::AllSimpleVariants),
325            // Untagged and all variants impl FromStr, but none **is** a
326            // String (i.e. irrefutably).
327            untagged_newtype_variants(
328                type_space,
329                &self.tag_type,
330                &self.variants,
331                TypeSpaceImpl::FromStr,
332                Some(TypeSpaceImpl::FromStringIrrefutable),
333            )
334            .then_some(TypeEntryEnumImpl::UntaggedFromStr),
335            // Untagged and all variants impl Display.
336            untagged_newtype_variants(
337                type_space,
338                &self.tag_type,
339                &self.variants,
340                TypeSpaceImpl::Display,
341                None,
342            )
343            .then_some(TypeEntryEnumImpl::UntaggedDisplay),
344            untagged_newtype_string(type_space, &self.tag_type, &self.variants)
345                .then_some(TypeEntryEnumImpl::UntaggedFromStringIrrefutable),
346        ]
347        .into_iter()
348        .flatten()
349        .collect();
350    }
351}
352
353impl Variant {
354    pub(crate) fn new(
355        raw_name: String,
356        description: Option<String>,
357        details: VariantDetails,
358    ) -> Self {
359        Self {
360            raw_name,
361            ident_name: None,
362            description,
363            details,
364        }
365    }
366}
367
368impl TypeEntryStruct {
369    pub(crate) fn from_metadata(
370        type_space: &TypeSpace,
371        type_name: Name,
372        metadata: &Option<Box<Metadata>>,
373        properties: Vec<StructProperty>,
374        deny_unknown_fields: bool,
375        schema: Schema,
376    ) -> TypeEntry {
377        let name = get_type_name(&type_name, metadata).unwrap();
378        let rename = None;
379        let description = metadata_description(metadata);
380        let default = metadata
381            .as_ref()
382            .and_then(|m| m.default.as_ref())
383            .cloned()
384            .map(WrappedValue::new);
385
386        let type_patch = TypePatch::new(type_space, name);
387
388        let details = TypeEntryDetails::Struct(Self {
389            name: type_patch.name,
390            rename,
391            description,
392            default,
393            properties,
394            deny_unknown_fields,
395            schema: SchemaWrapper(schema),
396        });
397
398        TypeEntry {
399            details,
400            extra_derives: type_patch.derives,
401            extra_attrs: type_patch.attrs,
402        }
403    }
404}
405
406impl TypeEntryNewtype {
407    pub(crate) fn from_metadata(
408        type_space: &TypeSpace,
409        type_name: Name,
410        metadata: &Option<Box<Metadata>>,
411        type_id: TypeId,
412        schema: Schema,
413    ) -> TypeEntry {
414        let name = get_type_name(&type_name, metadata).unwrap();
415        let rename = None;
416        let description = metadata_description(metadata);
417
418        let type_patch = TypePatch::new(type_space, name);
419
420        let details = TypeEntryDetails::Newtype(Self {
421            name: type_patch.name,
422            rename,
423            description,
424            default: None,
425            type_id,
426            constraints: TypeEntryNewtypeConstraints::None,
427            schema: SchemaWrapper(schema),
428        });
429
430        TypeEntry {
431            details,
432            extra_derives: type_patch.derives,
433            extra_attrs: type_patch.attrs,
434        }
435    }
436
437    pub(crate) fn from_metadata_with_enum_values(
438        type_space: &TypeSpace,
439        type_name: Name,
440        metadata: &Option<Box<Metadata>>,
441        type_id: TypeId,
442        enum_values: &[serde_json::Value],
443        schema: Schema,
444    ) -> TypeEntry {
445        let name = get_type_name(&type_name, metadata).unwrap();
446        let rename = None;
447        let description = metadata_description(metadata);
448
449        let type_patch = TypePatch::new(type_space, name);
450
451        let details = TypeEntryDetails::Newtype(Self {
452            name: type_patch.name,
453            rename,
454            description,
455            default: None,
456            type_id,
457            constraints: TypeEntryNewtypeConstraints::EnumValue(
458                enum_values.iter().cloned().map(WrappedValue::new).collect(),
459            ),
460            schema: SchemaWrapper(schema),
461        });
462
463        TypeEntry {
464            details,
465            extra_derives: type_patch.derives,
466            extra_attrs: type_patch.attrs,
467        }
468    }
469
470    pub(crate) fn from_metadata_with_deny_values(
471        type_space: &TypeSpace,
472        type_name: Name,
473        metadata: &Option<Box<Metadata>>,
474        type_id: TypeId,
475        enum_values: &[serde_json::Value],
476        schema: Schema,
477    ) -> TypeEntry {
478        let name = get_type_name(&type_name, metadata).unwrap();
479        let rename = None;
480        let description = metadata_description(metadata);
481
482        let type_patch = TypePatch::new(type_space, name);
483
484        let details = TypeEntryDetails::Newtype(Self {
485            name: type_patch.name,
486            rename,
487            description,
488            default: None,
489            type_id,
490            constraints: TypeEntryNewtypeConstraints::DenyValue(
491                enum_values.iter().cloned().map(WrappedValue::new).collect(),
492            ),
493            schema: SchemaWrapper(schema),
494        });
495
496        TypeEntry {
497            details,
498            extra_derives: type_patch.derives,
499            extra_attrs: type_patch.attrs,
500        }
501    }
502
503    pub(crate) fn from_metadata_with_string_validation(
504        type_space: &TypeSpace,
505        type_name: Name,
506        metadata: &Option<Box<Metadata>>,
507        type_id: TypeId,
508        validation: &schemars::schema::StringValidation,
509        schema: Schema,
510    ) -> TypeEntry {
511        let name = get_type_name(&type_name, metadata).unwrap();
512        let rename = None;
513        let description = metadata_description(metadata);
514
515        let schemars::schema::StringValidation {
516            max_length,
517            min_length,
518            pattern,
519        } = validation.clone();
520
521        let type_patch = TypePatch::new(type_space, name);
522
523        let details = TypeEntryDetails::Newtype(Self {
524            name: type_patch.name,
525            rename,
526            description,
527            default: None,
528            type_id,
529            constraints: TypeEntryNewtypeConstraints::String {
530                max_length,
531                min_length,
532                pattern,
533            },
534            schema: SchemaWrapper(schema),
535        });
536
537        TypeEntry {
538            details,
539            extra_derives: type_patch.derives,
540            extra_attrs: type_patch.attrs,
541        }
542    }
543}
544
545impl From<TypeEntryDetails> for TypeEntry {
546    fn from(details: TypeEntryDetails) -> Self {
547        Self {
548            details,
549            extra_derives: Default::default(),
550            extra_attrs: Default::default(),
551        }
552    }
553}
554
555impl TypeEntry {
556    pub(crate) fn new_native<S: ToString>(type_name: S, impls: &[TypeSpaceImpl]) -> Self {
557        TypeEntry {
558            details: TypeEntryDetails::Native(TypeEntryNative {
559                type_name: type_name.to_string(),
560                impls: impls.to_vec(),
561                parameters: Default::default(),
562            }),
563            extra_derives: Default::default(),
564            extra_attrs: Default::default(),
565        }
566    }
567    pub(crate) fn new_native_params<S: ToString>(type_name: S, params: &[TypeId]) -> Self {
568        TypeEntry {
569            details: TypeEntryDetails::Native(TypeEntryNative {
570                type_name: type_name.to_string(),
571                impls: Default::default(),
572                parameters: params.to_vec(),
573            }),
574            extra_derives: Default::default(),
575            extra_attrs: Default::default(),
576        }
577    }
578    pub(crate) fn new_boolean() -> Self {
579        TypeEntry {
580            details: TypeEntryDetails::Boolean,
581            extra_derives: Default::default(),
582            extra_attrs: Default::default(),
583        }
584    }
585    pub(crate) fn new_integer<S: ToString>(type_name: S) -> Self {
586        TypeEntryDetails::Integer(type_name.to_string()).into()
587    }
588    pub(crate) fn new_float<S: ToString>(type_name: S) -> Self {
589        TypeEntry {
590            details: TypeEntryDetails::Float(type_name.to_string()),
591            extra_derives: Default::default(),
592            extra_attrs: Default::default(),
593        }
594    }
595
596    pub(crate) fn finalize(&mut self, type_space: &mut TypeSpace) -> Result<()> {
597        if let TypeEntryDetails::Enum(enum_details) = &mut self.details {
598            enum_details.finalize(type_space);
599        }
600
601        self.check_defaults(type_space)
602    }
603
604    pub(crate) fn name(&self) -> Option<&String> {
605        match &self.details {
606            TypeEntryDetails::Enum(TypeEntryEnum { name, .. })
607            | TypeEntryDetails::Struct(TypeEntryStruct { name, .. })
608            | TypeEntryDetails::Newtype(TypeEntryNewtype { name, .. }) => Some(name),
609
610            _ => None,
611        }
612    }
613
614    pub(crate) fn has_impl<'a>(
615        &'a self,
616        type_space: &'a TypeSpace,
617        impl_name: TypeSpaceImpl,
618    ) -> bool {
619        match &self.details {
620            TypeEntryDetails::Enum(details) => match impl_name {
621                TypeSpaceImpl::Default => details.default.is_some(),
622                TypeSpaceImpl::FromStr => {
623                    details
624                        .bespoke_impls
625                        .contains(&TypeEntryEnumImpl::AllSimpleVariants)
626                        || details
627                            .bespoke_impls
628                            .contains(&TypeEntryEnumImpl::UntaggedFromStr)
629                }
630                TypeSpaceImpl::Display => {
631                    details
632                        .bespoke_impls
633                        .contains(&TypeEntryEnumImpl::AllSimpleVariants)
634                        || details
635                            .bespoke_impls
636                            .contains(&TypeEntryEnumImpl::UntaggedDisplay)
637                }
638                TypeSpaceImpl::FromStringIrrefutable => details
639                    .bespoke_impls
640                    .contains(&TypeEntryEnumImpl::UntaggedFromStringIrrefutable),
641            },
642
643            TypeEntryDetails::Struct(details) => match impl_name {
644                TypeSpaceImpl::Default => details.default.is_some(),
645                _ => false,
646            },
647            TypeEntryDetails::Newtype(details) => match (&details.constraints, impl_name) {
648                (_, TypeSpaceImpl::Default) => details.default.is_some(),
649                (TypeEntryNewtypeConstraints::String { .. }, TypeSpaceImpl::FromStr) => true,
650                (TypeEntryNewtypeConstraints::String { .. }, TypeSpaceImpl::Display) => true,
651                (TypeEntryNewtypeConstraints::None, _) => {
652                    // TODO this is a lucky kludge that will need to be removed
653                    // once we have proper handling of reference cycles (i.e.
654                    // as opposed to containment cycles... which we **do**
655                    // handle correctly). In particular output_newtype calls
656                    // this to determine if it should produce a FromStr impl.
657                    // This implementation could be infinitely recursive for a
658                    // type such as this:
659                    //     struct A(Box<A>);
660                    // While this type is useless and unusable, we do--
661                    // basically--support and test this. On such a type, if one
662                    // were to ask `ty.has_impl(TypeSpaceImpl::Default)` it
663                    // would be infinitely recursive. Fortunately the type
664                    // doesn't occur in the wild (we hope) and generation
665                    // doesn't rely on that particular query.
666
667                    let type_entry = type_space.id_to_entry.get(&details.type_id).unwrap();
668                    type_entry.has_impl(type_space, impl_name)
669                }
670                _ => false,
671            },
672            TypeEntryDetails::Native(details) => details.impls.contains(&impl_name),
673            TypeEntryDetails::Box(type_id) => {
674                if impl_name == TypeSpaceImpl::Default {
675                    let type_entry = type_space.id_to_entry.get(type_id).unwrap();
676                    type_entry.has_impl(type_space, impl_name)
677                } else {
678                    false
679                }
680            }
681
682            TypeEntryDetails::JsonValue => false,
683
684            TypeEntryDetails::Unit
685            | TypeEntryDetails::Option(_)
686            | TypeEntryDetails::Vec(_)
687            | TypeEntryDetails::Map(_, _)
688            | TypeEntryDetails::Set(_) => {
689                matches!(impl_name, TypeSpaceImpl::Default)
690            }
691
692            TypeEntryDetails::Tuple(type_ids) => {
693                // Default is implemented for tuples of up to 12 items long.
694                matches!(impl_name, TypeSpaceImpl::Default)
695                    && type_ids.len() <= 12
696                    && type_ids.iter().all(|type_id| {
697                        let type_entry = type_space.id_to_entry.get(type_id).unwrap();
698                        type_entry.has_impl(type_space, TypeSpaceImpl::Default)
699                    })
700            }
701
702            TypeEntryDetails::Array(item_id, length) => {
703                // Default is implemented for arrays of up to length 32.
704                if *length <= 32 && impl_name == TypeSpaceImpl::Default {
705                    let type_entry = type_space.id_to_entry.get(item_id).unwrap();
706                    type_entry.has_impl(type_space, impl_name)
707                } else {
708                    false
709                }
710            }
711
712            TypeEntryDetails::Boolean => match impl_name {
713                TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
714                TypeSpaceImpl::FromStringIrrefutable => false,
715            },
716            TypeEntryDetails::Integer(_) => match impl_name {
717                TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
718                TypeSpaceImpl::FromStringIrrefutable => false,
719            },
720
721            TypeEntryDetails::Float(_) => match impl_name {
722                TypeSpaceImpl::Default | TypeSpaceImpl::FromStr | TypeSpaceImpl::Display => true,
723                TypeSpaceImpl::FromStringIrrefutable => false,
724            },
725            TypeEntryDetails::String => match impl_name {
726                TypeSpaceImpl::Default
727                | TypeSpaceImpl::FromStr
728                | TypeSpaceImpl::Display
729                | TypeSpaceImpl::FromStringIrrefutable => true,
730            },
731
732            TypeEntryDetails::Reference(_) => unreachable!(),
733        }
734    }
735
736    pub(crate) fn output(&self, type_space: &TypeSpace, output: &mut OutputSpace) {
737        let derive_set = [
738            "::serde::Serialize",
739            "::serde::Deserialize",
740            "Debug",
741            "Clone",
742        ]
743        .into_iter()
744        .collect::<BTreeSet<_>>();
745
746        match &self.details {
747            TypeEntryDetails::Enum(enum_details) => {
748                self.output_enum(type_space, output, enum_details, derive_set)
749            }
750            TypeEntryDetails::Struct(struct_details) => {
751                self.output_struct(type_space, output, struct_details, derive_set)
752            }
753            TypeEntryDetails::Newtype(newtype_details) => {
754                self.output_newtype(type_space, output, newtype_details, derive_set)
755            }
756
757            // We should never get here as reference types should only be used
758            // in-flight, but never recorded into the type space.
759            TypeEntryDetails::Reference(_) => unreachable!(),
760
761            // Unnamed types require no definition as they're already defined.
762            _ => (),
763        }
764    }
765
766    fn output_enum(
767        &self,
768        type_space: &TypeSpace,
769        output: &mut OutputSpace,
770        enum_details: &TypeEntryEnum,
771        mut derive_set: BTreeSet<&str>,
772    ) {
773        let TypeEntryEnum {
774            name,
775            rename,
776            description,
777            default,
778            tag_type,
779            variants,
780            deny_unknown_fields,
781            bespoke_impls,
782            schema: SchemaWrapper(schema),
783        } = enum_details;
784
785        let doc = make_doc(name, description.as_ref(), schema);
786
787        // TODO this is a one-off for some useful traits; this should move into
788        // the creation of the enum type.
789        if variants
790            .iter()
791            .all(|variant| matches!(variant.details, VariantDetails::Simple))
792        {
793            derive_set.extend(["Copy", "PartialOrd", "Ord", "PartialEq", "Eq", "Hash"]);
794        }
795
796        let mut serde_options = Vec::new();
797        if let Some(old_name) = rename {
798            serde_options.push(quote! { rename = #old_name });
799        }
800        match tag_type {
801            EnumTagType::External => {}
802            EnumTagType::Internal { tag } => {
803                serde_options.push(quote! { tag = #tag });
804            }
805            EnumTagType::Adjacent { tag, content } => {
806                serde_options.push(quote! { tag = #tag });
807                serde_options.push(quote! { content = #content });
808            }
809            EnumTagType::Untagged => {
810                serde_options.push(quote! { untagged });
811            }
812        }
813        if *deny_unknown_fields {
814            serde_options.push(quote! { deny_unknown_fields });
815        }
816
817        let serde = (!serde_options.is_empty()).then(|| {
818            quote! { #[serde( #( #serde_options ),* )] }
819        });
820
821        let type_name = format_ident!("{}", name);
822
823        let variants_decl = variants
824            .iter()
825            .map(|variant| output_variant(variant, type_space, output, name))
826            .collect::<Vec<_>>();
827
828        // It should not be possible to construct an untagged enum
829        // with more than one simple variant--it would not be usable.
830        if tag_type == &EnumTagType::Untagged {
831            assert!(
832                variants
833                    .iter()
834                    .filter(|variant| matches!(variant.details, VariantDetails::Simple))
835                    .count()
836                    <= 1
837            )
838        }
839
840        // Display and FromStr impls for enums that are made exclusively of
841        // simple variants (and are not untagged).
842        let simple_enum_impl = bespoke_impls
843            .contains(&TypeEntryEnumImpl::AllSimpleVariants)
844            .then(|| {
845                let (match_variants, match_strs): (Vec<_>, Vec<_>) = variants
846                    .iter()
847                    .map(|variant| {
848                        let ident_name = variant.ident_name.as_ref().unwrap();
849                        let variant_name = format_ident!("{}", ident_name);
850                        (variant_name, &variant.raw_name)
851                    })
852                    .unzip();
853
854                quote! {
855                    impl ::std::fmt::Display for #type_name {
856                        fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
857                            match *self {
858                                #(Self::#match_variants => f.write_str(#match_strs),)*
859                            }
860                        }
861                    }
862                    impl ::std::str::FromStr for #type_name {
863                        type Err = self::error::ConversionError;
864
865                        fn from_str(value: &str) ->
866                            ::std::result::Result<Self, self::error::ConversionError>
867                        {
868                            match value {
869                                #(#match_strs => Ok(Self::#match_variants),)*
870                                _ => Err("invalid value".into()),
871                            }
872                        }
873                    }
874                    impl ::std::convert::TryFrom<&str> for #type_name {
875                        type Error = self::error::ConversionError;
876
877                        fn try_from(value: &str) ->
878                            ::std::result::Result<Self, self::error::ConversionError>
879                        {
880                            value.parse()
881                        }
882                    }
883                    impl ::std::convert::TryFrom<&::std::string::String> for #type_name {
884                        type Error = self::error::ConversionError;
885
886                        fn try_from(value: &::std::string::String) ->
887                            ::std::result::Result<Self, self::error::ConversionError>
888                        {
889                            value.parse()
890                        }
891                    }
892                    impl ::std::convert::TryFrom<::std::string::String> for #type_name {
893                        type Error = self::error::ConversionError;
894
895                        fn try_from(value: ::std::string::String) ->
896                            ::std::result::Result<Self, self::error::ConversionError>
897                        {
898                            value.parse()
899                        }
900                    }
901                }
902            });
903
904        let default_impl = default.as_ref().map(|value| {
905            let default_stream = self.output_value(type_space, &value.0, &quote! {}).unwrap();
906            quote! {
907                impl ::std::default::Default for #type_name {
908                    fn default() -> Self {
909                        #default_stream
910                    }
911                }
912            }
913        });
914
915        let untagged_newtype_from_string_impl = bespoke_impls
916            .contains(&TypeEntryEnumImpl::UntaggedFromStr)
917            .then(|| {
918                let variant_name = variants
919                    .iter()
920                    .map(|variant| format_ident!("{}", variant.ident_name.as_ref().unwrap()));
921
922                quote! {
923                    impl ::std::str::FromStr for #type_name {
924                        type Err = self::error::ConversionError;
925
926                        fn from_str(value: &str) ->
927                            ::std::result::Result<Self, self::error::ConversionError>
928                        {
929                            #(
930                                // Try to parse() into each variant.
931                                if let Ok(v) = value.parse() {
932                                    Ok(Self::#variant_name(v))
933                                } else
934                            )*
935                            {
936                                Err("string conversion failed for all variants".into())
937                            }
938                        }
939                    }
940                    impl ::std::convert::TryFrom<&str> for #type_name {
941                        type Error = self::error::ConversionError;
942
943                        fn try_from(value: &str) ->
944                            ::std::result::Result<Self, self::error::ConversionError>
945                        {
946                            value.parse()
947                        }
948                    }
949                    impl ::std::convert::TryFrom<&::std::string::String> for #type_name {
950                        type Error = self::error::ConversionError;
951
952                        fn try_from(value: &::std::string::String) ->
953                            ::std::result::Result<Self, self::error::ConversionError>
954                        {
955                            value.parse()
956                        }
957                    }
958                    impl ::std::convert::TryFrom<::std::string::String> for #type_name {
959                        type Error = self::error::ConversionError;
960
961                        fn try_from(value: ::std::string::String) ->
962                            ::std::result::Result<Self, self::error::ConversionError>
963                        {
964                            value.parse()
965                        }
966                    }
967                }
968            });
969
970        let untagged_newtype_to_string_impl = bespoke_impls
971            .contains(&TypeEntryEnumImpl::UntaggedDisplay)
972            .then(|| {
973                let variant_name = variants
974                    .iter()
975                    .map(|variant| format_ident!("{}", variant.ident_name.as_ref().unwrap()));
976
977                quote! {
978                    impl ::std::fmt::Display for #type_name {
979                        fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
980                            match self {
981                                #(Self::#variant_name(x) => x.fmt(f),)*
982                            }
983                        }
984                    }
985                }
986            });
987
988        let convenience_from = {
989            // Build a map whose key is the type ID or type IDs of the Item and
990            // Tuple variants, and whose value is a tuple of the original index
991            // and the variant itself. Any key that is seen multiple times has
992            // a value of None.
993            // TODO this requires more consideration to handle single-item
994            // tuples.
995            let unique_variants =
996                variants
997                    .iter()
998                    .enumerate()
999                    .fold(BTreeMap::new(), |mut map, (index, variant)| {
1000                        let key = match &variant.details {
1001                            VariantDetails::Item(type_id) => vec![type_id],
1002                            VariantDetails::Tuple(type_ids) => type_ids.iter().collect(),
1003                            _ => return map,
1004                        };
1005
1006                        map.entry(key)
1007                            .and_modify(|v| *v = None)
1008                            .or_insert(Some((index, variant)));
1009                        map
1010                    });
1011
1012            // Remove any variants that are duplicates (i.e. the value is None)
1013            // with the flatten(). Then order a new map according to the
1014            // original order of variants. The allows for the order to be
1015            // stable and for impl blocks to appear in the same order as their
1016            // corresponding variants.
1017            let ordered_variants = unique_variants
1018                .into_values()
1019                .flatten()
1020                .collect::<BTreeMap<_, _>>();
1021
1022            // Generate a `From<VariantType>` impl block that converts the type
1023            // into the appropriate variant of the enum.
1024            let variant_from = ordered_variants.into_values().map(|variant| {
1025                match &variant.details {
1026                    VariantDetails::Item(type_id) => {
1027                        let variant_type = type_space.id_to_entry.get(type_id).unwrap();
1028
1029                        // TODO Strings might conflict with the way we're
1030                        // dealing with TryFrom<String> right now.
1031                        (variant_type.details != TypeEntryDetails::String).then(|| {
1032                            let variant_type_ident = variant_type.type_ident(type_space, &None);
1033                            let variant_name =
1034                                format_ident!("{}", variant.ident_name.as_ref().unwrap());
1035                            quote! {
1036                                impl ::std::convert::From<#variant_type_ident> for #type_name {
1037                                    fn from(value: #variant_type_ident)
1038                                        -> Self
1039                                    {
1040                                        Self::#variant_name(value)
1041                                    }
1042                                }
1043                            }
1044                        })
1045                    }
1046                    VariantDetails::Tuple(type_ids) => {
1047                        let variant_type_idents = type_ids.iter().map(|type_id| {
1048                            type_space
1049                                .id_to_entry
1050                                .get(type_id)
1051                                .unwrap()
1052                                .type_ident(type_space, &None)
1053                        });
1054                        let variant_type_ident = if type_ids.len() != 1 {
1055                            quote! { ( #(#variant_type_idents),* ) }
1056                        } else {
1057                            // A single-item tuple requires a trailing
1058                            // comma.
1059                            quote! { ( #(#variant_type_idents,)* ) }
1060                        };
1061                        let variant_name =
1062                            format_ident!("{}", variant.ident_name.as_ref().unwrap());
1063                        let ii = (0..type_ids.len()).map(syn::Index::from);
1064                        Some(quote! {
1065                            impl ::std::convert::From<#variant_type_ident> for #type_name {
1066                                fn from(value: #variant_type_ident) -> Self {
1067                                    Self::#variant_name(
1068                                        #( value.#ii, )*
1069                                    )
1070                                }
1071                            }
1072                        })
1073                    }
1074                    _ => None,
1075                }
1076            });
1077
1078            quote! {
1079                #( #variant_from )*
1080            }
1081        };
1082
1083        let derives = strings_to_derives(
1084            derive_set,
1085            &self.extra_derives,
1086            &type_space.settings.extra_derives,
1087        );
1088
1089        let attrs = strings_to_attrs(&self.extra_attrs, &type_space.settings.extra_attrs);
1090
1091        let item = quote! {
1092            #doc
1093            #(#attrs)*
1094            #[derive(#(#derives),*)]
1095            #serde
1096            pub enum #type_name {
1097                #(#variants_decl)*
1098            }
1099
1100            #simple_enum_impl
1101            #default_impl
1102            #untagged_newtype_from_string_impl
1103            #untagged_newtype_to_string_impl
1104            #convenience_from
1105        };
1106        output.add_item(OutputSpaceMod::Crate, name, item);
1107    }
1108
1109    fn output_struct(
1110        &self,
1111        type_space: &TypeSpace,
1112        output: &mut OutputSpace,
1113        struct_details: &TypeEntryStruct,
1114        derive_set: BTreeSet<&str>,
1115    ) {
1116        enum PropDefault {
1117            None(String),
1118            Default(TokenStream),
1119            Custom(TokenStream),
1120        }
1121
1122        let TypeEntryStruct {
1123            name,
1124            rename,
1125            description,
1126            default,
1127            properties,
1128            deny_unknown_fields,
1129            schema: SchemaWrapper(schema),
1130        } = struct_details;
1131        let doc = make_doc(name, description.as_ref(), schema);
1132
1133        // Generate the serde directives as needed.
1134        let mut serde_options = Vec::new();
1135        if let Some(old_name) = rename {
1136            serde_options.push(quote! { rename = #old_name });
1137        }
1138        if *deny_unknown_fields {
1139            serde_options.push(quote! { deny_unknown_fields });
1140        }
1141        let serde =
1142            (!serde_options.is_empty()).then(|| quote! { #[serde( #( #serde_options ),* )] });
1143
1144        let type_name = format_ident!("{}", name);
1145
1146        // Gather the various components for all properties.
1147        let mut prop_doc = Vec::new();
1148        let mut prop_serde = Vec::new();
1149        let mut prop_default = Vec::new();
1150        let mut prop_name = Vec::new();
1151        let mut prop_error = Vec::new();
1152        let mut prop_type = Vec::new();
1153        let mut prop_type_scoped = Vec::new();
1154
1155        properties.iter().for_each(|prop| {
1156            prop_doc.push(prop.description.as_ref().map(|d| quote! { #[doc = #d] }));
1157            prop_name.push(format_ident!("{}", prop.name));
1158            prop_error.push(format!(
1159                "error converting supplied value for {}: {{e}}",
1160                prop.name,
1161            ));
1162
1163            let prop_type_entry = type_space.id_to_entry.get(&prop.type_id).unwrap();
1164            prop_type.push(prop_type_entry.type_ident(type_space, &None));
1165            prop_type_scoped
1166                .push(prop_type_entry.type_ident(type_space, &Some("super".to_string())));
1167
1168            let (serde, default_fn) = generate_serde_attr(
1169                name,
1170                &prop.name,
1171                &prop.rename,
1172                &prop.state,
1173                prop_type_entry,
1174                type_space,
1175                output,
1176            );
1177
1178            prop_serde.push(serde);
1179            prop_default.push(match default_fn {
1180                DefaultFunction::Default => PropDefault::Default(quote! {
1181                    Default::default()
1182                }),
1183                DefaultFunction::Custom(fn_name) => {
1184                    let default_fn = syn::parse_str::<Path>(&fn_name).unwrap();
1185                    PropDefault::Custom(quote! {
1186                        #default_fn()
1187                    })
1188                }
1189                DefaultFunction::None => {
1190                    let err_msg = format!("no value supplied for {}", prop.name);
1191                    PropDefault::None(err_msg)
1192                }
1193            });
1194        });
1195
1196        let derives = strings_to_derives(
1197            derive_set,
1198            &self.extra_derives,
1199            &type_space.settings.extra_derives,
1200        );
1201
1202        let attrs = strings_to_attrs(&self.extra_attrs, &type_space.settings.extra_attrs);
1203
1204        output.add_item(
1205            OutputSpaceMod::Crate,
1206            name,
1207            quote! {
1208                #doc
1209                #(#attrs)*
1210                #[derive(#(#derives),*)]
1211                #serde
1212                pub struct #type_name {
1213                    #(
1214                        #prop_doc
1215                        #prop_serde
1216                        pub #prop_name: #prop_type,
1217                    )*
1218                }
1219            },
1220        );
1221
1222        // If there's a default value, generate an impl Default
1223        if let Some(value) = default {
1224            let default_stream = self.output_value(type_space, &value.0, &quote! {}).unwrap();
1225            output.add_item(
1226                OutputSpaceMod::Crate,
1227                name,
1228                quote! {
1229                    impl ::std::default::Default for #type_name {
1230                        fn default() -> Self {
1231                            #default_stream
1232                        }
1233                    }
1234                },
1235            );
1236        } else if let Some(prop_default) = prop_default
1237            .iter()
1238            .map(|pd| match pd {
1239                PropDefault::None(_) => None,
1240                PropDefault::Default(token_stream) | PropDefault::Custom(token_stream) => {
1241                    Some(token_stream)
1242                }
1243            })
1244            .collect::<Option<Vec<_>>>()
1245        {
1246            // If all properties have a default, we can generate a Default impl
1247            output.add_item(
1248                OutputSpaceMod::Crate,
1249                name,
1250                quote! {
1251                    impl ::std::default::Default for #type_name {
1252                        fn default() -> Self {
1253                            Self {
1254                                #(
1255                                    #prop_name: #prop_default,
1256                                )*
1257                            }
1258                        }
1259                    }
1260                },
1261            )
1262        }
1263
1264        if type_space.settings.struct_builder {
1265            output.add_item(
1266                OutputSpaceMod::Crate,
1267                name,
1268                quote! {
1269                    impl #type_name {
1270                        pub fn builder() -> builder::#type_name {
1271                            Default::default()
1272                        }
1273                    }
1274                },
1275            );
1276
1277            // If there are no properties, all of this is kind of pointless,
1278            // but at least this lets us avoid the lint warning.
1279            let value_ident = if prop_name.is_empty() {
1280                quote! { _value }
1281            } else {
1282                quote! { value }
1283            };
1284
1285            let prop_default = prop_default.iter().map(|pd| match pd {
1286                PropDefault::None(err_msg) => quote! { Err(#err_msg.to_string()) },
1287                PropDefault::Default(default_fn) => quote! { Ok(#default_fn) },
1288                PropDefault::Custom(custom_fn) => quote! { Ok(super::#custom_fn) },
1289            });
1290
1291            output.add_item(
1292                OutputSpaceMod::Builder,
1293                name,
1294                quote! {
1295                    #[derive(Clone, Debug)]
1296                    pub struct #type_name {
1297                        #(
1298                            #prop_name: ::std::result::Result<#prop_type_scoped, ::std::string::String>,
1299                        )*
1300                    }
1301
1302                    impl ::std::default::Default for #type_name {
1303                        fn default() -> Self {
1304                            Self {
1305                                #(
1306                                    #prop_name: #prop_default,
1307                                )*
1308                            }
1309                        }
1310                    }
1311
1312                    impl #type_name {
1313                        #(
1314                            pub fn #prop_name<T>(mut self, value: T) -> Self
1315                                where
1316                                    T: ::std::convert::TryInto<#prop_type_scoped>,
1317                                    T::Error: ::std::fmt::Display,
1318                            {
1319                                self.#prop_name = value.try_into()
1320                                    .map_err(|e| format!(#prop_error));
1321                                self
1322                            }
1323                        )*
1324                    }
1325
1326                    // This is how the item is built.
1327                    impl ::std::convert::TryFrom<#type_name>
1328                        for super::#type_name
1329                    {
1330                        type Error = super::error::ConversionError;
1331
1332                        fn try_from(#value_ident: #type_name)
1333                            -> ::std::result::Result<Self, super::error::ConversionError>
1334                        {
1335                            Ok(Self {
1336                                #(
1337                                    #prop_name: value.#prop_name?,
1338                                )*
1339                            })
1340                        }
1341                    }
1342
1343                    // Construct a builder from the item.
1344                    impl ::std::convert::From<super::#type_name> for #type_name {
1345                        fn from(#value_ident: super::#type_name) -> Self {
1346                            Self {
1347                                #(
1348                                    #prop_name: Ok(value.#prop_name),
1349                                )*
1350                            }
1351                        }
1352                    }
1353                },
1354            );
1355        }
1356    }
1357
1358    fn output_newtype<'a>(
1359        &self,
1360        type_space: &'a TypeSpace,
1361        output: &mut OutputSpace,
1362        newtype_details: &TypeEntryNewtype,
1363        mut derive_set: BTreeSet<&'a str>,
1364    ) {
1365        let TypeEntryNewtype {
1366            name,
1367            rename: _,
1368            description,
1369            default,
1370            type_id,
1371            constraints,
1372            schema: SchemaWrapper(schema),
1373        } = newtype_details;
1374        let doc = make_doc(name, description.as_ref(), schema);
1375
1376        let type_name = format_ident!("{}", name);
1377        let inner_type = type_space.id_to_entry.get(type_id).unwrap();
1378        let inner_type_name = inner_type.type_ident(type_space, &None);
1379
1380        let is_str = matches!(inner_type.details, TypeEntryDetails::String);
1381
1382        // If this is just a wrapper around a string, we can derive some more
1383        // useful traits.
1384        if is_str {
1385            derive_set.extend(["PartialOrd", "Ord", "PartialEq", "Eq", "Hash"]);
1386        }
1387
1388        derive_set.extend(type_space.settings.extra_derives.iter().map(|s| s.as_str()));
1389
1390        let constraint_impl = match constraints {
1391            // In the unconstrained case we proxy impls through the inner type.
1392            TypeEntryNewtypeConstraints::None => {
1393                let str_impl = is_str.then(|| {
1394                    quote! {
1395                        impl ::std::str::FromStr for #type_name {
1396                            type Err = ::std::convert::Infallible;
1397
1398                            fn from_str(value: &str) ->
1399                                ::std::result::Result<Self, Self::Err>
1400                            {
1401                                Ok(Self(value.to_string()))
1402                            }
1403                        }
1404                    }
1405                });
1406
1407                // TODO see the comment in has_impl related to this case.
1408                let from_str_impl = (inner_type.has_impl(type_space, TypeSpaceImpl::FromStr)
1409                    && !is_str)
1410                    .then(|| {
1411                        quote! {
1412                            impl ::std::str::FromStr for #type_name {
1413                                type Err = <#inner_type_name as
1414                                    ::std::str::FromStr>::Err;
1415
1416                                fn from_str(value: &str) ->
1417                                    ::std::result::Result<Self, Self::Err>
1418                                {
1419                                    Ok(Self(value.parse()?))
1420                                }
1421                            }
1422                            impl ::std::convert::TryFrom<&str> for #type_name {
1423                                type Error = <#inner_type_name as
1424                                    ::std::str::FromStr>::Err;
1425
1426                                fn try_from(value: &str) ->
1427                                    ::std::result::Result<Self, Self::Error>
1428                                {
1429                                    value.parse()
1430                                }
1431                            }
1432                            impl ::std::convert::TryFrom<String> for #type_name {
1433                                type Error = <#inner_type_name as
1434                                    ::std::str::FromStr>::Err;
1435
1436                                fn try_from(value: String) ->
1437                                    ::std::result::Result<Self, Self::Error>
1438                                {
1439                                    value.parse()
1440                                }
1441                            }
1442                        }
1443                    });
1444
1445                let display_impl = inner_type
1446                    .has_impl(type_space, TypeSpaceImpl::Display)
1447                    .then(|| {
1448                        quote! {
1449                            impl ::std::fmt::Display for #type_name {
1450                                fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
1451                                    self.0.fmt(f)
1452                                }
1453                            }
1454                        }
1455                    });
1456
1457                quote! {
1458                    impl ::std::convert::From<#inner_type_name> for #type_name {
1459                        fn from(value: #inner_type_name) -> Self {
1460                            Self(value)
1461                        }
1462                    }
1463
1464                    #str_impl
1465                    #from_str_impl
1466                    #display_impl
1467                }
1468            }
1469
1470            TypeEntryNewtypeConstraints::DenyValue(enum_values)
1471            | TypeEntryNewtypeConstraints::EnumValue(enum_values) => {
1472                // Note that string types with enumerated values are converted
1473                // into simple enums rather than newtypes so we would not
1474                // expect to see a string as the inner type here.
1475                assert!(
1476                    matches!(constraints, TypeEntryNewtypeConstraints::DenyValue(_))
1477                        || !matches!(&inner_type.details, TypeEntryDetails::String)
1478                );
1479
1480                // We're going to impl Deserialize so we can remove it
1481                // from the set of derived impls.
1482                derive_set.remove("::serde::Deserialize");
1483
1484                let value_output = enum_values
1485                    .iter()
1486                    .map(|value| inner_type.output_value(type_space, &value.0, &quote! {}));
1487
1488                let value_string = enum_values
1489                    .iter()
1490                    .map(|value| serde_json::to_string(&value.0).unwrap());
1491
1492                // As with Deserialize, serde::JsonSchema requires a custom
1493                // impl. If it's present in the set of derives, remove it and
1494                // generate something that accurately models the type.
1495
1496                let has_json_schema = derive_set.remove("schemars::JsonSchema")
1497                    || derive_set.remove("::schemars::JsonSchema");
1498                let json_schema = has_json_schema.then(|| match constraints {
1499                    TypeEntryNewtypeConstraints::DenyValue(_) => quote! {
1500                        impl ::schemars::JsonSchema for #type_name {
1501                            fn schema_name() -> ::std::string::String {
1502                                #name.to_string()
1503                            }
1504
1505                            fn json_schema(gen: &mut ::schemars::gen::SchemaGenerator)
1506                                -> ::schemars::schema::Schema {
1507                                let mut schema =
1508                                    <#inner_type_name as ::schemars::JsonSchema>
1509                                        ::json_schema(gen)
1510                                        .into_object();
1511                                let not = ::schemars::schema::SchemaObject {
1512                                    enum_values: ::std::option::Option::Some([
1513                                        #( ::serde_json::from_str(#value_string).unwrap(), )*
1514                                    ].into_iter().collect()),
1515                                    ..::std::default::Default::default()
1516                                };
1517                                schema.subschemas().not = Some(
1518                                    ::std::boxed::Box::new(not.into())
1519                                );
1520                                schema.into()
1521                            }
1522                        }
1523                    },
1524                    TypeEntryNewtypeConstraints::EnumValue(_) => quote! {
1525                        impl ::schemars::JsonSchema for #type_name {
1526                            fn schema_name() -> ::std::string::String {
1527                                #name.to_string()
1528                            }
1529
1530                            fn json_schema(gen: &mut ::schemars::gen::SchemaGenerator)
1531                                -> ::schemars::schema::Schema {
1532                                let mut schema =
1533                                    <#inner_type_name as ::schemars::JsonSchema>
1534                                        ::json_schema(gen)
1535                                        .into_object();
1536                                schema.enum_values = ::std::option::Option::Some([
1537                                    #( ::serde_json::from_str(#value_string).unwrap(), )*
1538                                ].into_iter().collect());
1539                                schema.into()
1540                            }
1541                        }
1542                    },
1543
1544                    _ => unreachable!(),
1545                });
1546
1547                // TODO if the sub_type is a string we could probably impl
1548                // TryFrom<&str> as well and FromStr.
1549
1550                let not = matches!(constraints, TypeEntryNewtypeConstraints::EnumValue(_))
1551                    .then(|| quote! { ! });
1552
1553                quote! {
1554                    // This is effectively the constructor for this type.
1555                    impl ::std::convert::TryFrom<#inner_type_name> for #type_name {
1556                        type Error = self::error::ConversionError;
1557
1558                        fn try_from(
1559                            value: #inner_type_name
1560                        ) -> ::std::result::Result<Self, self::error::ConversionError>
1561                        {
1562                            if #not [
1563                                #(#value_output,)*
1564                            ].contains(&value) {
1565                                Err("invalid value".into())
1566                            } else {
1567                                Ok(Self(value))
1568                            }
1569                        }
1570                    }
1571
1572                    impl<'de> ::serde::Deserialize<'de> for #type_name {
1573                        fn deserialize<D>(
1574                            deserializer: D,
1575                        ) -> ::std::result::Result<Self, D::Error>
1576                        where
1577                            D: ::serde::Deserializer<'de>,
1578                        {
1579                            Self::try_from(
1580                                <#inner_type_name>::deserialize(deserializer)?,
1581                            )
1582                            .map_err(|e| {
1583                                <D::Error as ::serde::de::Error>::custom(
1584                                    e.to_string(),
1585                                )
1586                            })
1587                        }
1588                    }
1589
1590                    #json_schema
1591                }
1592            }
1593
1594            TypeEntryNewtypeConstraints::String {
1595                max_length,
1596                min_length,
1597                pattern,
1598            } => {
1599                let max = max_length.map(|v| {
1600                    let v = v as usize;
1601                    let err = format!("longer than {} characters", v);
1602                    quote! {
1603                        if value.chars().count() > #v {
1604                            return Err(#err.into());
1605                        }
1606                    }
1607                });
1608                let min = min_length.map(|v| {
1609                    let v = v as usize;
1610                    let err = format!("shorter than {} characters", v);
1611                    quote! {
1612                        if value.chars().count() < #v {
1613                            return Err(#err.into());
1614                        }
1615                    }
1616                });
1617
1618                let pat = pattern.as_ref().map(|p| {
1619                    let err = format!("doesn't match pattern \"{}\"", p);
1620                    quote! {
1621                        static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(|| {
1622                            ::regress::Regex::new(#p).unwrap()
1623                        });
1624                        if PATTERN.find(value).is_none() {
1625                            return Err(#err.into());
1626                        }
1627                    }
1628                });
1629
1630                // We're going to impl Deserialize so we can remove it
1631                // from the set of derived impls.
1632                derive_set.remove("::serde::Deserialize");
1633
1634                // TODO: if a user were to derive schemars::JsonSchema, it
1635                // wouldn't be accurate.
1636                quote! {
1637                    impl ::std::str::FromStr for #type_name {
1638                        type Err = self::error::ConversionError;
1639
1640                        fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
1641                            #max
1642                            #min
1643                            #pat
1644
1645                            Ok(Self(value.to_string()))
1646                        }
1647                    }
1648                    impl ::std::convert::TryFrom<&str> for #type_name {
1649                        type Error = self::error::ConversionError;
1650
1651                        fn try_from(value: &str) ->
1652                            ::std::result::Result<Self, self::error::ConversionError>
1653                        {
1654                            value.parse()
1655                        }
1656                    }
1657                    impl ::std::convert::TryFrom<&::std::string::String> for #type_name {
1658                        type Error = self::error::ConversionError;
1659
1660                        fn try_from(value: &::std::string::String) ->
1661                            ::std::result::Result<Self, self::error::ConversionError>
1662                        {
1663                            value.parse()
1664                        }
1665                    }
1666                    impl ::std::convert::TryFrom<::std::string::String> for #type_name {
1667                        type Error = self::error::ConversionError;
1668
1669                        fn try_from(value: ::std::string::String) ->
1670                            ::std::result::Result<Self, self::error::ConversionError>
1671                        {
1672                            value.parse()
1673                        }
1674                    }
1675
1676                    impl<'de> ::serde::Deserialize<'de> for #type_name {
1677                        fn deserialize<D>(
1678                            deserializer: D,
1679                        ) -> ::std::result::Result<Self, D::Error>
1680                        where
1681                            D: ::serde::Deserializer<'de>,
1682                        {
1683                            ::std::string::String::deserialize(deserializer)?
1684                            .parse()
1685                            .map_err(|e: self::error::ConversionError| {
1686                                <D::Error as ::serde::de::Error>::custom(
1687                                    e.to_string(),
1688                                )
1689                            })
1690                        }
1691                    }
1692                }
1693            }
1694        };
1695
1696        // If there are no constraints, let consumers directly access the value.
1697        let vis = match constraints {
1698            TypeEntryNewtypeConstraints::None => Some(quote! {pub}),
1699            _ => None,
1700        };
1701
1702        let default_impl = default.as_ref().map(|value| {
1703            let default_stream = self.output_value(type_space, &value.0, &quote! {}).unwrap();
1704            quote! {
1705                impl ::std::default::Default for #type_name {
1706                    fn default() -> Self {
1707                        #default_stream
1708                    }
1709                }
1710            }
1711        });
1712
1713        // This isn't the cleanest. Unlike other types, we roll in the
1714        // extra_derives here so that we can sniff out and override uses of
1715        // "schemars::JsonSchema".
1716        let derives = strings_to_derives(derive_set, &self.extra_derives, &[]);
1717
1718        let attrs = strings_to_attrs(&self.extra_attrs, &type_space.settings.extra_attrs);
1719
1720        let item = quote! {
1721            #doc
1722            #(#attrs)*
1723            #[derive(#(#derives),*)]
1724            #[serde(transparent)]
1725            pub struct #type_name(#vis #inner_type_name);
1726
1727            impl ::std::ops::Deref for #type_name {
1728                type Target = #inner_type_name;
1729                fn deref(&self) -> &#inner_type_name {
1730                    &self.0
1731                }
1732            }
1733
1734            impl ::std::convert::From<#type_name> for #inner_type_name {
1735                fn from(value: #type_name) -> Self {
1736                    value.0
1737                }
1738            }
1739
1740            #default_impl
1741            #constraint_impl
1742        };
1743        output.add_item(OutputSpaceMod::Crate, name, item);
1744    }
1745
1746    pub(crate) fn type_name(&self, type_space: &TypeSpace) -> String {
1747        self.type_ident(type_space, &None).to_string()
1748    }
1749
1750    pub(crate) fn type_ident(
1751        &self,
1752        type_space: &TypeSpace,
1753        type_mod: &Option<String>,
1754    ) -> TokenStream {
1755        match &self.details {
1756            // Named types.
1757            TypeEntryDetails::Enum(TypeEntryEnum { name, .. })
1758            | TypeEntryDetails::Struct(TypeEntryStruct { name, .. })
1759            | TypeEntryDetails::Newtype(TypeEntryNewtype { name, .. }) => match &type_mod {
1760                Some(type_mod) => {
1761                    let type_mod = format_ident!("{}", type_mod);
1762                    let type_name = format_ident!("{}", name);
1763                    quote! { #type_mod :: #type_name }
1764                }
1765                None => {
1766                    let type_name = format_ident!("{}", name);
1767                    quote! { #type_name }
1768                }
1769            },
1770
1771            TypeEntryDetails::Option(id) => {
1772                let inner_ty = type_space
1773                    .id_to_entry
1774                    .get(id)
1775                    .expect("unresolved type id for option");
1776                let inner_ident = inner_ty.type_ident(type_space, type_mod);
1777
1778                // Flatten nested Option types. This would only happen if the
1779                // schema encoded it; it's an odd construction.
1780                match &inner_ty.details {
1781                    TypeEntryDetails::Option(_) => inner_ident,
1782                    _ => quote! { ::std::option::Option<#inner_ident> },
1783                }
1784            }
1785
1786            TypeEntryDetails::Box(id) => {
1787                let inner_ty = type_space
1788                    .id_to_entry
1789                    .get(id)
1790                    .expect("unresolved type id for box");
1791
1792                let item = inner_ty.type_ident(type_space, type_mod);
1793
1794                quote! { ::std::boxed::Box<#item> }
1795            }
1796
1797            TypeEntryDetails::Vec(id) => {
1798                let inner_ty = type_space
1799                    .id_to_entry
1800                    .get(id)
1801                    .expect("unresolved type id for array");
1802                let item = inner_ty.type_ident(type_space, type_mod);
1803
1804                quote! { ::std::vec::Vec<#item> }
1805            }
1806
1807            TypeEntryDetails::Map(key_id, value_id) => {
1808                let map_to_use = &type_space.settings.map_type;
1809                let key_ty = type_space
1810                    .id_to_entry
1811                    .get(key_id)
1812                    .expect("unresolved type id for map key");
1813                let value_ty = type_space
1814                    .id_to_entry
1815                    .get(value_id)
1816                    .expect("unresolved type id for map value");
1817
1818                if key_ty.details == TypeEntryDetails::String
1819                    && value_ty.details == TypeEntryDetails::JsonValue
1820                {
1821                    quote! { ::serde_json::Map<::std::string::String, ::serde_json::Value> }
1822                } else {
1823                    let key_ident = key_ty.type_ident(type_space, type_mod);
1824                    let value_ident = value_ty.type_ident(type_space, type_mod);
1825                    let map_to_use = &map_to_use.0;
1826
1827                    quote! { #map_to_use<#key_ident, #value_ident> }
1828                }
1829            }
1830
1831            TypeEntryDetails::Set(id) => {
1832                let inner_ty = type_space
1833                    .id_to_entry
1834                    .get(id)
1835                    .expect("unresolved type id for set");
1836                let item = inner_ty.type_ident(type_space, type_mod);
1837                // TODO we'll want this to be a Set of some kind, but we need
1838                // to get the derives right first.
1839                quote! { Vec<#item> }
1840            }
1841
1842            TypeEntryDetails::Tuple(items) => {
1843                let type_idents = items.iter().map(|item| {
1844                    type_space
1845                        .id_to_entry
1846                        .get(item)
1847                        .expect("unresolved type id for tuple")
1848                        .type_ident(type_space, type_mod)
1849                });
1850
1851                if items.len() != 1 {
1852                    quote! { ( #(#type_idents),* ) }
1853                } else {
1854                    // A single-item tuple requires a trailing comma.
1855                    quote! { ( #(#type_idents,)* ) }
1856                }
1857            }
1858
1859            TypeEntryDetails::Array(item_id, length) => {
1860                let item_ty = type_space
1861                    .id_to_entry
1862                    .get(item_id)
1863                    .expect("unresolved type id for array");
1864                let item_ident = item_ty.type_ident(type_space, type_mod);
1865
1866                quote! { [#item_ident; #length]}
1867            }
1868
1869            TypeEntryDetails::Native(TypeEntryNative {
1870                type_name,
1871                impls: _,
1872                parameters,
1873            }) => {
1874                let path =
1875                    syn::parse_str::<syn::TypePath>(type_name).expect("type path wasn't valid");
1876
1877                let type_idents = (!parameters.is_empty()).then(|| {
1878                    let type_idents = parameters.iter().map(|type_id| {
1879                        type_space
1880                            .id_to_entry
1881                            .get(type_id)
1882                            .expect("unresolved type id for tuple")
1883                            .type_ident(type_space, type_mod)
1884                    });
1885                    quote! { < #(#type_idents,)* > }
1886                });
1887
1888                quote! {
1889                    #path
1890                    #type_idents
1891                }
1892            }
1893
1894            TypeEntryDetails::Unit => quote! { () },
1895            TypeEntryDetails::String => quote! { ::std::string::String },
1896            TypeEntryDetails::Boolean => quote! { bool },
1897            TypeEntryDetails::JsonValue => quote! { ::serde_json::Value },
1898            TypeEntryDetails::Integer(name) | TypeEntryDetails::Float(name) => {
1899                syn::parse_str::<syn::TypePath>(name)
1900                    .unwrap()
1901                    .to_token_stream()
1902            }
1903
1904            TypeEntryDetails::Reference(_) => panic!("references should be resolved by now"),
1905        }
1906    }
1907
1908    pub(crate) fn type_parameter_ident(
1909        &self,
1910        type_space: &TypeSpace,
1911        lifetime_name: Option<&str>,
1912    ) -> TokenStream {
1913        let lifetime = lifetime_name.map(|s| {
1914            vec![
1915                TokenTree::from(Punct::new('\'', Spacing::Joint)),
1916                TokenTree::from(format_ident!("{}", s)),
1917            ]
1918            .into_iter()
1919            .collect::<TokenStream>()
1920        });
1921        match &self.details {
1922            // We special-case enums for which all variants are simple to let
1923            // them be passed as values rather than as references.
1924            // TODO we should probably cache "simpleness" of all variants
1925            // rather than iterating every time. We'll know it when the enum is
1926            // constructed.
1927            TypeEntryDetails::Enum(TypeEntryEnum { variants, .. })
1928                if variants
1929                    .iter()
1930                    .all(|variant| matches!(&variant.details, VariantDetails::Simple)) =>
1931            {
1932                self.type_ident(type_space, &type_space.settings.type_mod)
1933            }
1934            TypeEntryDetails::Enum(_)
1935            | TypeEntryDetails::Struct(_)
1936            | TypeEntryDetails::Newtype(_)
1937            | TypeEntryDetails::Vec(_)
1938            | TypeEntryDetails::Map(..)
1939            | TypeEntryDetails::Set(_)
1940            | TypeEntryDetails::Box(_)
1941            | TypeEntryDetails::Native(_)
1942            | TypeEntryDetails::Array(..)
1943            | TypeEntryDetails::JsonValue => {
1944                let ident = self.type_ident(type_space, &type_space.settings.type_mod);
1945                quote! {
1946                    & #lifetime #ident
1947                }
1948            }
1949
1950            TypeEntryDetails::Option(id) => {
1951                let inner_ty = type_space
1952                    .id_to_entry
1953                    .get(id)
1954                    .expect("unresolved type id for option");
1955                let inner_ident = inner_ty.type_parameter_ident(type_space, lifetime_name);
1956
1957                // Flatten nested Option types. This would only happen if the
1958                // schema encoded it; it's an odd construction.
1959                match &inner_ty.details {
1960                    TypeEntryDetails::Option(_) => inner_ident,
1961                    _ => quote! { Option<#inner_ident> },
1962                }
1963            }
1964
1965            TypeEntryDetails::Tuple(items) => {
1966                let type_streams = items.iter().map(|item| {
1967                    type_space
1968                        .id_to_entry
1969                        .get(item)
1970                        .expect("unresolved type id for tuple")
1971                        .type_parameter_ident(type_space, lifetime_name)
1972                });
1973
1974                if items.len() != 1 {
1975                    quote! { ( #(#type_streams),* ) }
1976                } else {
1977                    // Single-element tuples require special handling. In
1978                    // particular, they must have a trailing comma or else are
1979                    // treated as extraneously parenthesized types.
1980                    quote! { ( #(#type_streams,)* ) }
1981                }
1982            }
1983
1984            TypeEntryDetails::Unit
1985            | TypeEntryDetails::Boolean
1986            | TypeEntryDetails::Integer(_)
1987            | TypeEntryDetails::Float(_) => {
1988                self.type_ident(type_space, &type_space.settings.type_mod)
1989            }
1990            TypeEntryDetails::String => quote! { & #lifetime str },
1991
1992            TypeEntryDetails::Reference(_) => panic!("references should be resolved by now"),
1993        }
1994    }
1995
1996    pub(crate) fn describe(&self) -> String {
1997        match &self.details {
1998            TypeEntryDetails::Enum(TypeEntryEnum { name, .. }) => format!("enum {}", name),
1999            TypeEntryDetails::Struct(TypeEntryStruct { name, .. }) => format!("struct {}", name),
2000            TypeEntryDetails::Newtype(TypeEntryNewtype { name, type_id, .. }) => {
2001                format!("newtype {} {}", name, type_id.0)
2002            }
2003
2004            TypeEntryDetails::Unit => "()".to_string(),
2005            TypeEntryDetails::Option(type_id) => format!("option {}", type_id.0),
2006            TypeEntryDetails::Vec(type_id) => format!("vec {}", type_id.0),
2007            TypeEntryDetails::Map(key_id, value_id) => {
2008                format!("map {} {}", key_id.0, value_id.0)
2009            }
2010            TypeEntryDetails::Set(type_id) => format!("set {}", type_id.0),
2011            TypeEntryDetails::Box(type_id) => format!("box {}", type_id.0),
2012            TypeEntryDetails::Tuple(type_ids) => {
2013                format!(
2014                    "tuple ({})",
2015                    type_ids
2016                        .iter()
2017                        .map(|type_id| type_id.0.to_string())
2018                        .collect::<Vec<String>>()
2019                        .join(", ")
2020                )
2021            }
2022            TypeEntryDetails::Array(type_id, length) => {
2023                format!("array {}; {}", type_id.0, length)
2024            }
2025            TypeEntryDetails::Boolean => "bool".to_string(),
2026            TypeEntryDetails::Native(TypeEntryNative {
2027                type_name: name, ..
2028            })
2029            | TypeEntryDetails::Integer(name)
2030            | TypeEntryDetails::Float(name) => name.clone(),
2031            TypeEntryDetails::String => "string".to_string(),
2032
2033            TypeEntryDetails::JsonValue => "json value".to_string(),
2034
2035            TypeEntryDetails::Reference(_) => unreachable!(),
2036        }
2037    }
2038}
2039
2040fn make_doc(name: &str, description: Option<&String>, schema: &Schema) -> TokenStream {
2041    let desc = match description {
2042        Some(desc) => desc,
2043        None => &format!("`{}`", name),
2044    };
2045    let schema_json = serde_json::to_string_pretty(schema).unwrap();
2046    let schema_lines = schema_json.lines();
2047    quote! {
2048        #[doc = #desc]
2049        ///
2050        /// <details><summary>JSON schema</summary>
2051        ///
2052        /// ```json
2053        #(
2054            #[doc = #schema_lines]
2055        )*
2056        /// ```
2057        /// </details>
2058    }
2059}
2060
2061fn strings_to_derives<'a>(
2062    derive_set: BTreeSet<&'a str>,
2063    type_derives: &'a BTreeSet<String>,
2064    extra_derives: &'a [String],
2065) -> impl Iterator<Item = TokenStream> + 'a {
2066    let mut combined_derives = derive_set.clone();
2067    combined_derives.extend(extra_derives.iter().map(String::as_str));
2068    combined_derives.extend(type_derives.iter().map(String::as_str));
2069    combined_derives.into_iter().map(|derive| {
2070        syn::parse_str::<syn::Path>(derive)
2071            .unwrap()
2072            .into_token_stream()
2073    })
2074}
2075
2076fn strings_to_attrs<'a>(
2077    type_attrs: &'a BTreeSet<String>,
2078    extra_attrs: &'a [String],
2079) -> impl Iterator<Item = TokenStream> + 'a {
2080    let mut combined_attrs = BTreeSet::new();
2081    combined_attrs.extend(extra_attrs.iter().map(String::as_str));
2082    combined_attrs.extend(type_attrs.iter().map(String::as_str));
2083    combined_attrs
2084        .into_iter()
2085        .map(|attr| attr.parse::<TokenStream>().unwrap())
2086}
2087
2088/// Returns true iff...
2089/// - the enum is untagged
2090/// - all variants are single items (aka newtype variants)
2091/// - the type of the newtype variant implements the required trait
2092fn untagged_newtype_variants(
2093    type_space: &TypeSpace,
2094    tag_type: &EnumTagType,
2095    variants: &[Variant],
2096    req_impl: TypeSpaceImpl,
2097    neg_impl: Option<TypeSpaceImpl>,
2098) -> bool {
2099    tag_type == &EnumTagType::Untagged
2100        && variants.iter().all(|variant| {
2101            // If the variant is a single item...
2102            match &variant.details {
2103                VariantDetails::Item(type_id) => Some(type_id),
2104                _ => None,
2105            }
2106            .map_or_else(
2107                || false,
2108                |type_id| {
2109                    let type_entry = type_space.id_to_entry.get(type_id).unwrap();
2110                    // ... and its type has the required impl
2111                    type_entry.has_impl(type_space, req_impl)
2112                        && neg_impl
2113                            .is_none_or(|neg_impl| !type_entry.has_impl(type_space, neg_impl))
2114                },
2115            )
2116        })
2117}
2118
2119/// Returns true iff...
2120/// - the enum is untagged
2121/// - **any** variant is a single items **and** it is irrefutably a string
2122fn untagged_newtype_string(
2123    type_space: &TypeSpace,
2124    tag_type: &EnumTagType,
2125    variants: &[Variant],
2126) -> bool {
2127    tag_type == &EnumTagType::Untagged
2128        && variants.iter().any(|variant| {
2129            // If the variant is a single item...
2130            match &variant.details {
2131                VariantDetails::Item(type_id) => Some(type_id),
2132                _ => None,
2133            }
2134            .map_or_else(
2135                || false,
2136                |type_id| {
2137                    let type_entry = type_space.id_to_entry.get(type_id).unwrap();
2138                    // ... and it is irrefutably a string
2139                    type_entry.has_impl(type_space, TypeSpaceImpl::FromStringIrrefutable)
2140                },
2141            )
2142        })
2143}
2144
2145#[cfg(test)]
2146mod tests {
2147    use crate::{
2148        type_entry::{SchemaWrapper, TypeEntry, TypeEntryStruct},
2149        TypeEntryDetails, TypeSpace,
2150    };
2151
2152    #[test]
2153    fn test_ident() {
2154        let ts = TypeSpace::default();
2155
2156        let type_mod = Some("the_mod".to_string());
2157
2158        let t = TypeEntry::new_integer("u32");
2159        let ident = t.type_ident(&ts, &type_mod);
2160        assert_eq!(ident.to_string(), "u32");
2161        let parameter = t.type_parameter_ident(&ts, None);
2162        assert_eq!(parameter.to_string(), "u32");
2163
2164        let t = TypeEntry::from(TypeEntryDetails::String);
2165        let ident = t.type_ident(&ts, &type_mod);
2166        assert_eq!(ident.to_string(), ":: std :: string :: String");
2167        let parameter = t.type_parameter_ident(&ts, None);
2168        assert_eq!(parameter.to_string(), "& str");
2169        let parameter = t.type_parameter_ident(&ts, Some("static"));
2170        assert_eq!(parameter.to_string(), "& 'static str");
2171
2172        let t = TypeEntry::from(TypeEntryDetails::Unit);
2173        let ident = t.type_ident(&ts, &type_mod);
2174        assert_eq!(ident.to_string(), "()");
2175        let parameter = t.type_parameter_ident(&ts, None);
2176        assert_eq!(parameter.to_string(), "()");
2177
2178        let t = TypeEntry::from(TypeEntryDetails::Struct(TypeEntryStruct {
2179            name: "SomeType".to_string(),
2180            rename: None,
2181            description: None,
2182            default: None,
2183            properties: vec![],
2184            deny_unknown_fields: false,
2185            schema: SchemaWrapper(schemars::schema::Schema::Bool(false)),
2186        }));
2187
2188        let ident = t.type_ident(&ts, &type_mod);
2189        assert_eq!(ident.to_string(), "the_mod :: SomeType");
2190        let parameter = t.type_parameter_ident(&ts, None);
2191        assert_eq!(parameter.to_string(), "& SomeType");
2192        let parameter = t.type_parameter_ident(&ts, Some("a"));
2193        assert_eq!(parameter.to_string(), "& 'a SomeType");
2194    }
2195}