1#![deny(missing_docs)]
6
7use std::collections::{BTreeMap, BTreeSet};
8
9use conversions::SchemaCache;
10use log::{debug, info};
11use output::OutputSpace;
12use proc_macro2::TokenStream;
13use quote::{format_ident, quote, ToTokens};
14use schemars::schema::{Metadata, RootSchema, Schema};
15use thiserror::Error;
16use type_entry::{
17 StructPropertyState, TypeEntry, TypeEntryDetails, TypeEntryNative, TypeEntryNewtype,
18 WrappedValue,
19};
20
21use crate::util::{sanitize, Case};
22
23pub use crate::util::accept_as_ident;
24
25#[cfg(test)]
26mod test_util;
27
28mod conversions;
29mod convert;
30mod cycles;
31mod defaults;
32mod enums;
33mod merge;
34mod output;
35mod rust_extension;
36mod structs;
37mod type_entry;
38mod util;
39mod validate;
40mod value;
41
42#[allow(missing_docs)]
43#[derive(Error, Debug)]
44pub enum Error {
45 #[error("unexpected value type")]
46 BadValue(String, serde_json::Value),
47 #[error("invalid TypeId")]
48 InvalidTypeId,
49 #[error("value does not conform to the given schema")]
50 InvalidValue,
51 #[error("invalid schema for {}: {reason}", show_type_name(.type_name.as_deref()))]
52 InvalidSchema {
53 type_name: Option<String>,
54 reason: String,
55 },
56}
57
58impl Error {
59 fn invalid_value() -> Self {
60 Self::InvalidValue
61 }
62}
63
64#[allow(missing_docs)]
65pub type Result<T> = std::result::Result<T, Error>;
66
67fn show_type_name(type_name: Option<&str>) -> &str {
68 type_name.unwrap_or("<unknown type>")
69}
70
71#[derive(Debug)]
73pub struct Type<'a> {
74 type_space: &'a TypeSpace,
75 type_entry: &'a TypeEntry,
76}
77
78#[allow(missing_docs)]
79pub enum TypeDetails<'a> {
81 Enum(TypeEnum<'a>),
82 Struct(TypeStruct<'a>),
83 Newtype(TypeNewtype<'a>),
84
85 Option(TypeId),
86 Vec(TypeId),
87 Map(TypeId, TypeId),
88 Set(TypeId),
89 Box(TypeId),
90 Tuple(Box<dyn Iterator<Item = TypeId> + 'a>),
91 Array(TypeId, usize),
92 Builtin(&'a str),
93
94 Unit,
95 String,
96}
97
98pub struct TypeEnum<'a> {
100 details: &'a type_entry::TypeEntryEnum,
101}
102
103pub enum TypeEnumVariant<'a> {
105 Simple,
107 Tuple(Vec<TypeId>),
109 Struct(Vec<(&'a str, TypeId)>),
111}
112
113pub struct TypeEnumVariantInfo<'a> {
115 pub name: &'a str,
117 pub description: Option<&'a str>,
119 pub details: TypeEnumVariant<'a>,
121}
122
123pub struct TypeStruct<'a> {
125 details: &'a type_entry::TypeEntryStruct,
126}
127
128pub struct TypeStructPropInfo<'a> {
130 pub name: &'a str,
132 pub description: Option<&'a str>,
134 pub required: bool,
136 pub type_id: TypeId,
138}
139
140pub struct TypeNewtype<'a> {
142 details: &'a type_entry::TypeEntryNewtype,
143}
144
145#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Hash)]
147pub struct TypeId(u64);
148
149#[derive(Debug, Clone, PartialEq)]
150pub(crate) enum Name {
151 Required(String),
152 Suggested(String),
153 Unknown,
154}
155
156impl Name {
157 pub fn into_option(self) -> Option<String> {
158 match self {
159 Name::Required(s) | Name::Suggested(s) => Some(s),
160 Name::Unknown => None,
161 }
162 }
163
164 pub fn append(&self, s: &str) -> Self {
165 match self {
166 Name::Required(prefix) | Name::Suggested(prefix) => {
167 Self::Suggested(format!("{}_{}", prefix, s))
168 }
169 Name::Unknown => Name::Unknown,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
175pub(crate) enum RefKey {
176 Root,
177 Def(String),
178}
179
180#[derive(Debug)]
182pub struct TypeSpace {
183 next_id: u64,
184
185 definitions: BTreeMap<RefKey, Schema>,
190
191 id_to_entry: BTreeMap<TypeId, TypeEntry>,
192 type_to_id: BTreeMap<TypeEntryDetails, TypeId>,
193
194 name_to_id: BTreeMap<String, TypeId>,
195 ref_to_id: BTreeMap<RefKey, TypeId>,
196
197 uses_chrono: bool,
198 uses_uuid: bool,
199 uses_serde_json: bool,
200 uses_regress: bool,
201
202 settings: TypeSpaceSettings,
203
204 cache: SchemaCache,
205
206 defaults: BTreeSet<DefaultImpl>,
208}
209
210impl Default for TypeSpace {
211 fn default() -> Self {
212 Self {
213 next_id: 1,
214 definitions: Default::default(),
215 id_to_entry: Default::default(),
216 type_to_id: Default::default(),
217 name_to_id: Default::default(),
218 ref_to_id: Default::default(),
219 uses_chrono: Default::default(),
220 uses_uuid: Default::default(),
221 uses_serde_json: Default::default(),
222 uses_regress: Default::default(),
223 settings: Default::default(),
224 cache: Default::default(),
225 defaults: Default::default(),
226 }
227 }
228}
229
230#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
231pub(crate) enum DefaultImpl {
232 Boolean,
233 I64,
234 U64,
235 NZU64,
236}
237
238#[derive(Clone)]
240pub struct MapType(pub syn::Type);
241
242impl MapType {
243 pub fn new(s: &str) -> Self {
251 let map_type = syn::parse_str::<syn::Type>(s).expect("valid ident");
252 Self(map_type)
253 }
254}
255
256impl std::str::FromStr for MapType {
257 type Err = String;
258
259 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
260 let map_type = syn::parse_str::<syn::Type>(s)
261 .map_err(|err| format!("invalid map type {s:?}: {err}"))?;
262 Ok(Self(map_type))
263 }
264}
265
266impl Default for MapType {
267 fn default() -> Self {
268 Self::new("::std::collections::HashMap")
269 }
270}
271
272impl std::fmt::Debug for MapType {
273 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
274 write!(f, "MapType({})", self.0.to_token_stream())
275 }
276}
277
278impl std::fmt::Display for MapType {
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 self.0.to_token_stream().fmt(f)
281 }
282}
283
284impl<'de> serde::Deserialize<'de> for MapType {
285 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
286 where
287 D: serde::Deserializer<'de>,
288 {
289 let s = String::deserialize(deserializer)?;
290 s.parse().map_err(serde::de::Error::custom)
291 }
292}
293
294impl From<String> for MapType {
295 fn from(s: String) -> Self {
301 Self::new(&s)
302 }
303}
304
305impl From<&str> for MapType {
306 fn from(s: &str) -> Self {
312 Self::new(s)
313 }
314}
315
316impl From<syn::Type> for MapType {
317 fn from(t: syn::Type) -> Self {
318 Self(t)
319 }
320}
321
322#[derive(Default, Debug, Clone)]
324pub struct TypeSpaceSettings {
325 type_mod: Option<String>,
326 extra_derives: Vec<String>,
327 extra_attrs: Vec<String>,
328 struct_builder: bool,
329
330 unknown_crates: UnknownPolicy,
331 crates: BTreeMap<String, CrateSpec>,
332 map_type: MapType,
333
334 patch: BTreeMap<String, TypeSpacePatch>,
335 replace: BTreeMap<String, TypeSpaceReplace>,
336 convert: Vec<TypeSpaceConversion>,
337}
338
339#[derive(Debug, Clone)]
340struct CrateSpec {
341 version: CrateVers,
342 rename: Option<String>,
343}
344
345#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Deserialize)]
348pub enum UnknownPolicy {
349 #[default]
351 Generate,
352 Allow,
358 Deny,
364}
365
366#[derive(Debug, Clone)]
369pub enum CrateVers {
370 Version(semver::Version),
372 Any,
374 Never,
376}
377
378impl CrateVers {
379 pub fn parse(s: &str) -> Option<Self> {
381 if s == "!" {
382 Some(Self::Never)
383 } else if s == "*" {
384 Some(Self::Any)
385 } else {
386 Some(Self::Version(semver::Version::parse(s).ok()?))
387 }
388 }
389}
390
391#[derive(Debug, Default, Clone)]
393pub struct TypeSpacePatch {
394 rename: Option<String>,
395 derives: Vec<String>,
396 attrs: Vec<String>,
397}
398
399#[derive(Debug, Default, Clone)]
401pub struct TypeSpaceReplace {
402 replace_type: String,
403 impls: Vec<TypeSpaceImpl>,
404}
405
406#[derive(Debug, Clone)]
409struct TypeSpaceConversion {
410 schema: schemars::schema::SchemaObject,
411 type_name: String,
412 impls: Vec<TypeSpaceImpl>,
413}
414
415#[allow(missing_docs)]
416#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
419#[non_exhaustive]
420pub enum TypeSpaceImpl {
421 FromStr,
422 FromStringIrrefutable,
423 Display,
424 Default,
425}
426
427impl std::str::FromStr for TypeSpaceImpl {
428 type Err = String;
429
430 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
431 match s {
432 "FromStr" => Ok(Self::FromStr),
433 "Display" => Ok(Self::Display),
434 "Default" => Ok(Self::Default),
435 _ => Err(format!("{} is not a valid trait specifier", s)),
436 }
437 }
438}
439
440impl TypeSpaceSettings {
441 pub fn with_type_mod<S: AsRef<str>>(&mut self, type_mod: S) -> &mut Self {
443 self.type_mod = Some(type_mod.as_ref().to_string());
444 self
445 }
446
447 pub fn with_derive(&mut self, derive: String) -> &mut Self {
449 if !self.extra_derives.contains(&derive) {
450 self.extra_derives.push(derive);
451 }
452 self
453 }
454
455 pub fn with_attr(&mut self, attr: String) -> &mut Self {
457 if !self.extra_attrs.contains(&attr) {
458 self.extra_attrs.push(attr);
459 }
460 self
461 }
462
463 pub fn with_struct_builder(&mut self, struct_builder: bool) -> &mut Self {
465 self.struct_builder = struct_builder;
466 self
467 }
468
469 pub fn with_replacement<TS: ToString, RS: ToString, I: Iterator<Item = TypeSpaceImpl>>(
473 &mut self,
474 type_name: TS,
475 replace_type: RS,
476 impls: I,
477 ) -> &mut Self {
478 self.replace.insert(
479 type_name.to_string(),
480 TypeSpaceReplace {
481 replace_type: replace_type.to_string(),
482 impls: impls.collect(),
483 },
484 );
485 self
486 }
487
488 pub fn with_patch<S: ToString>(
493 &mut self,
494 type_name: S,
495 type_patch: &TypeSpacePatch,
496 ) -> &mut Self {
497 self.patch.insert(type_name.to_string(), type_patch.clone());
498 self
499 }
500
501 pub fn with_conversion<S: ToString, I: Iterator<Item = TypeSpaceImpl>>(
527 &mut self,
528 schema: schemars::schema::SchemaObject,
529 type_name: S,
530 impls: I,
531 ) -> &mut Self {
532 self.convert.push(TypeSpaceConversion {
533 schema,
534 type_name: type_name.to_string(),
535 impls: impls.collect(),
536 });
537 self
538 }
539
540 pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
545 self.unknown_crates = policy;
546 self
547 }
548
549 pub fn with_crate<S1: ToString>(
557 &mut self,
558 crate_name: S1,
559 version: CrateVers,
560 rename: Option<&String>,
561 ) -> &mut Self {
562 self.crates.insert(
563 crate_name.to_string(),
564 CrateSpec {
565 version,
566 rename: rename.cloned(),
567 },
568 );
569 self
570 }
571
572 pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
587 self.map_type = map_type.into();
588 self
589 }
590}
591
592impl TypeSpacePatch {
593 pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
595 self.rename = Some(rename.to_string());
596 self
597 }
598
599 pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
601 self.derives.push(derive.to_string());
602 self
603 }
604
605 pub fn with_attr<S: ToString>(&mut self, attr: S) -> &mut Self {
607 self.attrs.push(attr.to_string());
608 self
609 }
610}
611
612impl TypeSpace {
613 pub fn new(settings: &TypeSpaceSettings) -> Self {
615 let mut cache = SchemaCache::default();
616
617 settings.convert.iter().for_each(
618 |TypeSpaceConversion {
619 schema,
620 type_name,
621 impls,
622 }| {
623 cache.insert(schema, type_name, impls);
624 },
625 );
626
627 Self {
628 settings: settings.clone(),
629 cache,
630 ..Default::default()
631 }
632 }
633
634 pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
644 where
645 I: IntoIterator<Item = (S, Schema)>,
646 S: AsRef<str>,
647 {
648 self.add_ref_types_impl(
649 type_defs
650 .into_iter()
651 .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
652 )
653 }
654
655 fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
656 where
657 I: IntoIterator<Item = (RefKey, Schema)>,
658 {
659 let definitions = type_defs.into_iter().collect::<Vec<_>>();
661
662 let base_id = self.next_id;
665 let def_len = definitions.len() as u64;
666 self.next_id += def_len;
667
668 for (index, (ref_name, schema)) in definitions.iter().enumerate() {
669 self.ref_to_id
670 .insert(ref_name.clone(), TypeId(base_id + index as u64));
671 self.definitions.insert(ref_name.clone(), schema.clone());
672 }
673
674 for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
679 info!(
680 "converting type: {:?} with schema {}",
681 ref_name,
682 serde_json::to_string(&schema).unwrap()
683 );
684
685 let type_id = TypeId(base_id + index as u64);
688
689 let maybe_replace = match &ref_name {
690 RefKey::Root => None,
691 RefKey::Def(def_name) => {
692 let check_name = sanitize(def_name, Case::Pascal);
693 self.settings.replace.get(&check_name)
694 }
695 };
696
697 match maybe_replace {
698 None => {
699 let type_name = if let RefKey::Def(name) = ref_name {
700 Name::Required(name.clone())
701 } else {
702 Name::Unknown
703 };
704 self.convert_ref_type(type_name, schema, type_id)?
705 }
706
707 Some(replace_type) => {
708 let type_entry = TypeEntry::new_native(
709 replace_type.replace_type.clone(),
710 &replace_type.impls.clone(),
711 );
712 self.id_to_entry.insert(type_id, type_entry);
713 }
714 }
715 }
716
717 self.break_cycles(base_id..base_id + def_len);
720
721 for index in base_id..self.next_id {
723 let type_id = TypeId(index);
724 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
725 debug!("finalizing type entry: {} {:#?}", index, &type_entry);
726 type_entry.finalize(self)?;
727 self.id_to_entry.insert(type_id, type_entry);
728 }
729
730 Ok(())
731 }
732
733 fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
734 let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
735 let default = metadata
736 .as_ref()
737 .and_then(|m| m.default.as_ref())
738 .cloned()
739 .map(WrappedValue::new);
740 let type_entry = match &mut type_entry.details {
741 TypeEntryDetails::Enum(details) => {
743 details.default = default;
744 type_entry
745 }
746 TypeEntryDetails::Struct(details) => {
747 details.default = default;
748 type_entry
749 }
750 TypeEntryDetails::Newtype(details) => {
751 details.default = default;
752 type_entry
753 }
754
755 TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
760 self,
761 type_name,
762 metadata,
763 type_id.clone(),
764 schema.clone(),
765 ),
766
767 TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
768
769 _ => {
772 info!(
773 "type alias {:?} {}\n{:?}",
774 type_name,
775 serde_json::to_string_pretty(&schema).unwrap(),
776 metadata
777 );
778 let subtype_id = self.assign_type(type_entry);
779 TypeEntryNewtype::from_metadata(
780 self,
781 type_name,
782 metadata,
783 subtype_id,
784 schema.clone(),
785 )
786 }
787 };
788 if let Some(entry_name) = type_entry.name() {
790 self.name_to_id.insert(entry_name.clone(), type_id.clone());
791 }
792 self.id_to_entry.insert(type_id, type_entry);
793 Ok(())
794 }
795
796 pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
799 self.add_type_with_name(schema, None)
800 }
801
802 pub fn add_type_with_name(
805 &mut self,
806 schema: &Schema,
807 name_hint: Option<String>,
808 ) -> Result<TypeId> {
809 let base_id = self.next_id;
810
811 let name = match name_hint {
812 Some(s) => Name::Suggested(s),
813 None => Name::Unknown,
814 };
815 let (type_id, _) = self.id_for_schema(name, schema)?;
816
817 for index in base_id..self.next_id {
819 let type_id = TypeId(index);
820 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
821 type_entry.finalize(self)?;
822 self.id_to_entry.insert(type_id, type_entry);
823 }
824
825 Ok(type_id)
826 }
827
828 pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
832 let RootSchema {
833 meta_schema: _,
834 schema,
835 definitions,
836 } = schema;
837
838 let mut defs = definitions
839 .into_iter()
840 .map(|(key, schema)| (RefKey::Def(key), schema))
841 .collect::<Vec<_>>();
842
843 let root_type = schema
845 .metadata
846 .as_ref()
847 .and_then(|m| m.title.as_ref())
848 .is_some();
849
850 if root_type {
851 defs.push((RefKey::Root, schema.into()));
852 }
853
854 self.add_ref_types_impl(defs)?;
855
856 if root_type {
857 Ok(self.ref_to_id.get(&RefKey::Root).cloned())
858 } else {
859 Ok(None)
860 }
861 }
862
863 pub fn get_type(&self, type_id: &TypeId) -> Result<Type<'_>> {
865 let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
866 Ok(Type {
867 type_space: self,
868 type_entry,
869 })
870 }
871
872 pub fn uses_chrono(&self) -> bool {
874 self.uses_chrono
875 }
876
877 pub fn uses_regress(&self) -> bool {
879 self.uses_regress
880 }
881
882 pub fn uses_serde_json(&self) -> bool {
884 self.uses_serde_json
885 }
886
887 pub fn uses_uuid(&self) -> bool {
889 self.uses_uuid
890 }
891
892 pub fn iter_types(&self) -> impl Iterator<Item = Type<'_>> {
895 self.id_to_entry.values().map(move |type_entry| Type {
896 type_space: self,
897 type_entry,
898 })
899 }
900
901 pub fn to_stream(&self) -> TokenStream {
903 let mut output = OutputSpace::default();
904
905 output.add_item(
908 output::OutputSpaceMod::Error,
909 "",
910 quote! {
911 pub struct ConversionError(::std::borrow::Cow<'static, str>);
913
914 impl ::std::error::Error for ConversionError {}
915 impl ::std::fmt::Display for ConversionError {
916 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
917 -> Result<(), ::std::fmt::Error>
918 {
919 ::std::fmt::Display::fmt(&self.0, f)
920 }
921 }
922
923 impl ::std::fmt::Debug for ConversionError {
924 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
925 -> Result<(), ::std::fmt::Error>
926 {
927 ::std::fmt::Debug::fmt(&self.0, f)
928 }
929 }
930 impl From<&'static str> for ConversionError {
931 fn from(value: &'static str) -> Self {
932 Self(value.into())
933 }
934 }
935 impl From<String> for ConversionError {
936 fn from(value: String) -> Self {
937 Self(value.into())
938 }
939 }
940 },
941 );
942
943 self.id_to_entry
945 .values()
946 .for_each(|type_entry| type_entry.output(self, &mut output));
947
948 self.defaults
950 .iter()
951 .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
952
953 output.into_stream()
954 }
955
956 fn assign(&mut self) -> TypeId {
958 let id = TypeId(self.next_id);
959 self.next_id += 1;
960 id
961 }
962
963 fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
968 if let TypeEntryDetails::Reference(type_id) = ty.details {
969 type_id
970 } else if let Some(name) = ty.name() {
971 if let Some(type_id) = self.name_to_id.get(name) {
981 type_id.clone()
987 } else {
988 let type_id = self.assign();
989 self.name_to_id.insert(name.clone(), type_id.clone());
990 self.id_to_entry.insert(type_id.clone(), ty);
991 type_id
992 }
993 } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
994 type_id.clone()
995 } else {
996 let type_id = self.assign();
997 self.type_to_id.insert(ty.details.clone(), type_id.clone());
998 self.id_to_entry.insert(type_id.clone(), ty);
999 type_id
1000 }
1001 }
1002
1003 fn id_for_schema<'a>(
1008 &mut self,
1009 type_name: Name,
1010 schema: &'a Schema,
1011 ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
1012 let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
1013 if let Some(metadata) = metadata {
1014 let default = metadata.default.clone().map(WrappedValue::new);
1015 match &mut type_entry.details {
1016 TypeEntryDetails::Enum(details) => {
1017 details.default = default;
1018 }
1019 TypeEntryDetails::Struct(details) => {
1020 details.default = default;
1021 }
1022 TypeEntryDetails::Newtype(details) => {
1023 details.default = default;
1024 }
1025 _ => (),
1026 }
1027 }
1028 let type_id = self.assign_type(type_entry);
1029 Ok((type_id, metadata))
1030 }
1031
1032 fn id_to_option(&mut self, id: &TypeId) -> TypeId {
1034 self.assign_type(TypeEntryDetails::Option(id.clone()).into())
1035 }
1036
1037 fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
1039 TypeEntryDetails::Option(self.assign_type(ty)).into()
1040 }
1041
1042 fn id_to_box(&mut self, id: &TypeId) -> TypeId {
1044 self.assign_type(TypeEntryDetails::Box(id.clone()).into())
1045 }
1046}
1047
1048impl ToTokens for TypeSpace {
1049 fn to_tokens(&self, tokens: &mut TokenStream) {
1050 tokens.extend(self.to_stream())
1051 }
1052}
1053
1054impl Type<'_> {
1055 pub fn name(&self) -> String {
1057 let Type {
1058 type_space,
1059 type_entry,
1060 } = self;
1061 type_entry.type_name(type_space)
1062 }
1063
1064 pub fn ident(&self) -> TokenStream {
1067 let Type {
1068 type_space,
1069 type_entry,
1070 } = self;
1071 type_entry.type_ident(type_space, &type_space.settings.type_mod)
1072 }
1073
1074 pub fn parameter_ident(&self) -> TokenStream {
1078 let Type {
1079 type_space,
1080 type_entry,
1081 } = self;
1082 type_entry.type_parameter_ident(type_space, None)
1083 }
1084
1085 pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1090 let Type {
1091 type_space,
1092 type_entry,
1093 } = self;
1094 type_entry.type_parameter_ident(type_space, Some(lifetime))
1095 }
1096
1097 pub fn describe(&self) -> String {
1099 self.type_entry.describe()
1100 }
1101
1102 pub fn details(&self) -> TypeDetails<'_> {
1104 match &self.type_entry.details {
1105 TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1107 TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1108 TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1109
1110 TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1112 TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1113 TypeEntryDetails::Map(key_id, value_id) => {
1114 TypeDetails::Map(key_id.clone(), value_id.clone())
1115 }
1116 TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1117 TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1118 TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1119 TypeEntryDetails::Array(type_id, length) => {
1120 TypeDetails::Array(type_id.clone(), *length)
1121 }
1122
1123 TypeEntryDetails::Unit => TypeDetails::Unit,
1125 TypeEntryDetails::Native(TypeEntryNative {
1126 type_name: name, ..
1127 })
1128 | TypeEntryDetails::Integer(name)
1129 | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1130 TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1131 TypeEntryDetails::String => TypeDetails::String,
1132 TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1133
1134 TypeEntryDetails::Reference(_) => unreachable!(),
1136 }
1137 }
1138
1139 pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1141 let Type {
1142 type_space,
1143 type_entry,
1144 } = self;
1145 type_entry.has_impl(type_space, impl_name)
1146 }
1147
1148 pub fn builder(&self) -> Option<TokenStream> {
1150 let Type {
1151 type_space,
1152 type_entry,
1153 } = self;
1154
1155 if !type_space.settings.struct_builder {
1156 return None;
1157 }
1158
1159 match &type_entry.details {
1160 TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1161 match &type_space.settings.type_mod {
1162 Some(type_mod) => {
1163 let type_mod = format_ident!("{}", type_mod);
1164 let type_name = format_ident!("{}", name);
1165 Some(quote! { #type_mod :: builder :: #type_name })
1166 }
1167 None => {
1168 let type_name = format_ident!("{}", name);
1169 Some(quote! { builder :: #type_name })
1170 }
1171 }
1172 }
1173 _ => None,
1174 }
1175 }
1176}
1177
1178impl<'a> TypeEnum<'a> {
1179 pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1181 self.variants_info().map(|info| (info.name, info.details))
1182 }
1183
1184 pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1186 self.details.variants.iter().map(move |variant| {
1187 let details = match &variant.details {
1188 type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1189 type_entry::VariantDetails::Item(type_id) => {
1192 TypeEnumVariant::Tuple(vec![type_id.clone()])
1193 }
1194 type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1195 type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1196 properties
1197 .iter()
1198 .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1199 .collect(),
1200 ),
1201 };
1202 TypeEnumVariantInfo {
1203 name: variant.ident_name.as_ref().unwrap(),
1204 description: variant.description.as_deref(),
1205 details,
1206 }
1207 })
1208 }
1209}
1210
1211impl<'a> TypeStruct<'a> {
1212 pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1214 self.details
1215 .properties
1216 .iter()
1217 .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1218 }
1219
1220 pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo<'a>> {
1222 self.details
1223 .properties
1224 .iter()
1225 .map(move |prop| TypeStructPropInfo {
1226 name: prop.name.as_str(),
1227 description: prop.description.as_deref(),
1228 required: matches!(&prop.state, StructPropertyState::Required),
1229 type_id: prop.type_id.clone(),
1230 })
1231 }
1232}
1233
1234impl TypeNewtype<'_> {
1235 pub fn inner(&self) -> TypeId {
1237 self.details.type_id.clone()
1238 }
1239}
1240
1241#[cfg(test)]
1242mod tests {
1243 use schema::Schema;
1244 use schemars::{schema_for, JsonSchema};
1245 use serde::Serialize;
1246 use serde_json::json;
1247 use std::collections::HashSet;
1248
1249 use crate::{
1250 output::OutputSpace,
1251 test_util::validate_output,
1252 type_entry::{TypeEntryEnum, VariantDetails},
1253 MapType, Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1254 };
1255
1256 #[test]
1257 fn test_map_type_from_str() {
1258 let map_type = "::std::collections::BTreeMap".parse::<MapType>().unwrap();
1259 assert_eq!(map_type.to_string(), ":: std :: collections :: BTreeMap");
1260
1261 "not a valid!!type".parse::<MapType>().unwrap_err();
1262 "".parse::<MapType>().unwrap_err();
1263 }
1264
1265 #[test]
1266 fn test_map_type_deserialize() {
1267 let map_type: MapType =
1268 serde_json::from_value(json!("::std::collections::BTreeMap")).unwrap();
1269 assert_eq!(map_type.to_string(), ":: std :: collections :: BTreeMap");
1270
1271 let map_type: MapType =
1274 serde_json::from_str("\"::std::collections::\\u0042TreeMap\"").unwrap();
1275 assert_eq!(map_type.to_string(), ":: std :: collections :: BTreeMap");
1276
1277 serde_json::from_value::<MapType>(json!("not a valid!!type")).unwrap_err();
1279 }
1280
1281 #[allow(dead_code)]
1282 #[derive(Serialize, JsonSchema)]
1283 struct Blah {
1284 blah: String,
1285 }
1286
1287 #[allow(dead_code)]
1288 #[derive(Serialize, JsonSchema)]
1289 #[serde(rename_all = "camelCase")]
1290 enum E {
1293 A,
1295 B,
1297 C(Blah),
1300 D {
1302 dd: String,
1304 },
1305 }
1313
1314 #[allow(dead_code)]
1315 #[derive(JsonSchema)]
1316 #[serde(rename_all = "camelCase")]
1317 struct Foo {
1318 #[serde(default)]
1320 bar: Option<String>,
1321 baz_baz: i32,
1322 e: E,
1324 }
1325
1326 #[test]
1327 fn test_simple() {
1328 let schema = schema_for!(Foo);
1329 println!("{:#?}", schema);
1330 let mut type_space = TypeSpace::default();
1331 type_space.add_ref_types(schema.definitions).unwrap();
1332 let (ty, _) = type_space
1333 .convert_schema_object(
1334 Name::Unknown,
1335 &schemars::schema::Schema::Object(schema.schema.clone()),
1336 &schema.schema,
1337 )
1338 .unwrap();
1339
1340 println!("{:#?}", ty);
1341
1342 let mut output = OutputSpace::default();
1343 ty.output(&type_space, &mut output);
1344 println!("{}", output.into_stream());
1345
1346 for ty in type_space.id_to_entry.values() {
1347 println!("{:#?}", ty);
1348 let mut output = OutputSpace::default();
1349 ty.output(&type_space, &mut output);
1350 println!("{}", output.into_stream());
1351 }
1352 }
1353
1354 #[test]
1355 fn test_external_references() {
1356 let schema = json!({
1357 "$schema": "http://json-schema.org/draft-04/schema#",
1358 "definitions": {
1359 "somename": {
1360 "$ref": "#/definitions/someothername",
1361 "required": [ "someproperty" ]
1362 },
1363 "someothername": {
1364 "type": "object",
1365 "properties": {
1366 "someproperty": {
1367 "type": "string"
1368 }
1369 }
1370 }
1371 }
1372 });
1373 let schema = serde_json::from_value(schema).unwrap();
1374 println!("{:#?}", schema);
1375 let settings = TypeSpaceSettings::default();
1376 let mut type_space = TypeSpace::new(&settings);
1377 type_space.add_root_schema(schema).unwrap();
1378 let tokens = type_space.to_stream().to_string();
1379 println!("{}", tokens);
1380 assert!(tokens
1381 .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1382 }
1383
1384 #[test]
1385 fn test_convert_enum_string() {
1386 #[allow(dead_code)]
1387 #[derive(JsonSchema)]
1388 #[serde(rename_all = "camelCase")]
1389 enum SimpleEnum {
1390 DotCom,
1391 Grizz,
1392 Kenneth,
1393 }
1394
1395 let schema = schema_for!(SimpleEnum);
1396 println!("{:#?}", schema);
1397
1398 let mut type_space = TypeSpace::default();
1399 type_space.add_ref_types(schema.definitions).unwrap();
1400 let (ty, _) = type_space
1401 .convert_schema_object(
1402 Name::Unknown,
1403 &schemars::schema::Schema::Object(schema.schema.clone()),
1404 &schema.schema,
1405 )
1406 .unwrap();
1407
1408 match ty.details {
1409 TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1410 for variant in &variants {
1411 assert_eq!(variant.details, VariantDetails::Simple);
1412 }
1413 let var_names = variants
1414 .iter()
1415 .map(|variant| variant.ident_name.as_ref().unwrap().clone())
1416 .collect::<HashSet<_>>();
1417 assert_eq!(
1418 var_names,
1419 ["DotCom", "Grizz", "Kenneth",]
1420 .iter()
1421 .map(ToString::to_string)
1422 .collect::<HashSet<_>>()
1423 );
1424 }
1425 _ => {
1426 let mut output = OutputSpace::default();
1427 ty.output(&type_space, &mut output);
1428 println!("{}", output.into_stream());
1429 panic!();
1430 }
1431 }
1432 }
1433
1434 #[test]
1435 fn test_string_enum_with_null() {
1436 let original_schema = json!({ "$ref": "xxx"});
1437 let enum_values = vec![
1438 json!("Shadrach"),
1439 json!("Meshach"),
1440 json!("Abednego"),
1441 json!(null),
1442 ];
1443
1444 let mut type_space = TypeSpace::default();
1445 let (te, _) = type_space
1446 .convert_enum_string(
1447 Name::Required("OnTheGo".to_string()),
1448 &serde_json::from_value(original_schema).unwrap(),
1449 &None,
1450 &enum_values,
1451 None,
1452 )
1453 .unwrap();
1454
1455 if let TypeEntryDetails::Option(id) = &te.details {
1456 let ote = type_space.id_to_entry.get(id).unwrap();
1457 if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1458 let variants = variants
1459 .iter()
1460 .map(|v| match v.details {
1461 VariantDetails::Simple => v.ident_name.as_ref().unwrap().clone(),
1462 _ => panic!("unexpected variant type"),
1463 })
1464 .collect::<HashSet<_>>();
1465
1466 assert_eq!(
1467 variants,
1468 enum_values
1469 .iter()
1470 .flat_map(|j| j.as_str().map(ToString::to_string))
1471 .collect::<HashSet<_>>()
1472 );
1473 } else {
1474 panic!("not the sub-type we expected {:#?}", te)
1475 }
1476 } else {
1477 panic!("not the type we expected {:#?}", te)
1478 }
1479 }
1480
1481 #[test]
1482 fn test_alias() {
1483 #[allow(dead_code)]
1484 #[derive(JsonSchema, Schema)]
1485 struct Stuff(Vec<String>);
1486
1487 #[allow(dead_code)]
1488 #[derive(JsonSchema, Schema)]
1489 struct Things {
1490 a: String,
1491 b: Stuff,
1492 }
1493
1494 validate_output::<Things>();
1495 }
1496
1497 #[test]
1498 fn test_builder_name() {
1499 #[allow(dead_code)]
1500 #[derive(JsonSchema)]
1501 struct TestStruct {
1502 x: u32,
1503 }
1504
1505 let mut type_space = TypeSpace::default();
1506 let schema = schema_for!(TestStruct);
1507 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1508 let ty = type_space.get_type(&type_id).unwrap();
1509
1510 assert!(ty.builder().is_none());
1511
1512 let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1513 let schema = schema_for!(TestStruct);
1514 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1515 let ty = type_space.get_type(&type_id).unwrap();
1516
1517 assert_eq!(
1518 ty.builder().map(|ts| ts.to_string()),
1519 Some("builder :: TestStruct".to_string())
1520 );
1521
1522 let mut type_space = TypeSpace::new(
1523 TypeSpaceSettings::default()
1524 .with_type_mod("types")
1525 .with_struct_builder(true),
1526 );
1527 let schema = schema_for!(TestStruct);
1528 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1529 let ty = type_space.get_type(&type_id).unwrap();
1530
1531 assert_eq!(
1532 ty.builder().map(|ts| ts.to_string()),
1533 Some("types :: builder :: TestStruct".to_string())
1534 );
1535
1536 #[allow(dead_code)]
1537 #[derive(JsonSchema)]
1538 enum TestEnum {
1539 X,
1540 Y,
1541 }
1542 let mut type_space = TypeSpace::new(
1543 TypeSpaceSettings::default()
1544 .with_type_mod("types")
1545 .with_struct_builder(true),
1546 );
1547 let schema = schema_for!(TestEnum);
1548 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1549 let ty = type_space.get_type(&type_id).unwrap();
1550 assert!(ty.builder().is_none());
1551 }
1552}