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