1#![deny(missing_docs)]
6
7use std::collections::{BTreeMap, BTreeSet};
8
9use conversions::SchemaCache;
10use log::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
23#[cfg(test)]
24mod test_util;
25
26mod conversions;
27mod convert;
28mod cycles;
29mod defaults;
30mod enums;
31mod merge;
32mod output;
33mod rust_extension;
34mod structs;
35mod type_entry;
36mod util;
37mod validate;
38mod value;
39
40#[allow(missing_docs)]
41#[derive(Error, Debug)]
42pub enum Error {
43 #[error("unexpected value type")]
44 BadValue(String, serde_json::Value),
45 #[error("invalid TypeId")]
46 InvalidTypeId,
47 #[error("value does not conform to the given schema")]
48 InvalidValue,
49 #[error("invalid schema for {}: {reason}", show_type_name(.type_name.as_deref()))]
50 InvalidSchema {
51 type_name: Option<String>,
52 reason: String,
53 },
54}
55
56impl Error {
57 fn invalid_value() -> Self {
58 Self::InvalidValue
59 }
60}
61
62#[allow(missing_docs)]
63pub type Result<T> = std::result::Result<T, Error>;
64
65fn show_type_name(type_name: Option<&str>) -> &str {
66 if let Some(type_name) = type_name {
67 type_name
68 } else {
69 "<unknown type>"
70 }
71}
72
73#[derive(Debug)]
75pub struct Type<'a> {
76 type_space: &'a TypeSpace,
77 type_entry: &'a TypeEntry,
78}
79
80#[allow(missing_docs)]
81pub enum TypeDetails<'a> {
83 Enum(TypeEnum<'a>),
84 Struct(TypeStruct<'a>),
85 Newtype(TypeNewtype<'a>),
86
87 Option(TypeId),
88 Vec(TypeId),
89 Map(TypeId, TypeId),
90 Set(TypeId),
91 Box(TypeId),
92 Tuple(Box<dyn Iterator<Item = TypeId> + 'a>),
93 Array(TypeId, usize),
94 Builtin(&'a str),
95
96 Unit,
97 String,
98}
99
100pub struct TypeEnum<'a> {
102 details: &'a type_entry::TypeEntryEnum,
103}
104
105pub enum TypeEnumVariant<'a> {
107 Simple,
109 Tuple(Vec<TypeId>),
111 Struct(Vec<(&'a str, TypeId)>),
113}
114
115pub struct TypeEnumVariantInfo<'a> {
117 pub name: &'a str,
119 pub description: Option<&'a str>,
121 pub details: TypeEnumVariant<'a>,
123}
124
125pub struct TypeStruct<'a> {
127 details: &'a type_entry::TypeEntryStruct,
128}
129
130pub struct TypeStructPropInfo<'a> {
132 pub name: &'a str,
134 pub description: Option<&'a str>,
136 pub required: bool,
138 pub type_id: TypeId,
140}
141
142pub struct TypeNewtype<'a> {
144 details: &'a type_entry::TypeEntryNewtype,
145}
146
147#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Hash)]
149pub struct TypeId(u64);
150
151#[derive(Debug, Clone, PartialEq)]
152pub(crate) enum Name {
153 Required(String),
154 Suggested(String),
155 Unknown,
156}
157
158impl Name {
159 pub fn into_option(self) -> Option<String> {
160 match self {
161 Name::Required(s) | Name::Suggested(s) => Some(s),
162 Name::Unknown => None,
163 }
164 }
165
166 pub fn append(&self, s: &str) -> Self {
167 match self {
168 Name::Required(prefix) | Name::Suggested(prefix) => {
169 Self::Suggested(format!("{}_{}", prefix, s))
170 }
171 Name::Unknown => Name::Unknown,
172 }
173 }
174}
175
176#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
177pub(crate) enum RefKey {
178 Root,
179 Def(String),
180}
181
182#[derive(Debug)]
184pub struct TypeSpace {
185 next_id: u64,
186
187 definitions: BTreeMap<RefKey, Schema>,
192
193 id_to_entry: BTreeMap<TypeId, TypeEntry>,
194 type_to_id: BTreeMap<TypeEntryDetails, TypeId>,
195
196 name_to_id: BTreeMap<String, TypeId>,
197 ref_to_id: BTreeMap<RefKey, TypeId>,
198
199 uses_chrono: bool,
200 uses_uuid: bool,
201 uses_serde_json: bool,
202 uses_regress: bool,
203
204 settings: TypeSpaceSettings,
205
206 cache: SchemaCache,
207
208 defaults: BTreeSet<DefaultImpl>,
210}
211
212impl Default for TypeSpace {
213 fn default() -> Self {
214 Self {
215 next_id: 1,
216 definitions: Default::default(),
217 id_to_entry: Default::default(),
218 type_to_id: Default::default(),
219 name_to_id: Default::default(),
220 ref_to_id: Default::default(),
221 uses_chrono: Default::default(),
222 uses_uuid: Default::default(),
223 uses_serde_json: Default::default(),
224 uses_regress: Default::default(),
225 settings: Default::default(),
226 cache: Default::default(),
227 defaults: Default::default(),
228 }
229 }
230}
231
232#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
233pub(crate) enum DefaultImpl {
234 Boolean,
235 I64,
236 U64,
237 NZU64,
238}
239
240#[derive(Clone)]
242pub struct MapType(pub syn::Type);
243
244impl MapType {
245 pub fn new(s: &str) -> Self {
247 let map_type = syn::parse_str::<syn::Type>(s).expect("valid ident");
248 Self(map_type)
249 }
250}
251
252impl Default for MapType {
253 fn default() -> Self {
254 Self::new("::std::collections::HashMap")
255 }
256}
257
258impl std::fmt::Debug for MapType {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 write!(f, "MapType({})", self.0.to_token_stream())
261 }
262}
263
264impl std::fmt::Display for MapType {
265 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266 self.0.to_token_stream().fmt(f)
267 }
268}
269
270impl<'de> serde::Deserialize<'de> for MapType {
271 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
272 where
273 D: serde::Deserializer<'de>,
274 {
275 let s = <&str>::deserialize(deserializer)?;
276 Ok(Self::new(s))
277 }
278}
279
280impl From<String> for MapType {
281 fn from(s: String) -> Self {
282 Self::new(&s)
283 }
284}
285
286impl From<&str> for MapType {
287 fn from(s: &str) -> Self {
288 Self::new(s)
289 }
290}
291
292impl From<syn::Type> for MapType {
293 fn from(t: syn::Type) -> Self {
294 Self(t)
295 }
296}
297
298#[derive(Default, Debug, Clone)]
300pub struct TypeSpaceSettings {
301 type_mod: Option<String>,
302 extra_derives: Vec<String>,
303 struct_builder: bool,
304
305 unknown_crates: UnknownPolicy,
306 crates: BTreeMap<String, CrateSpec>,
307 map_type: MapType,
308
309 patch: BTreeMap<String, TypeSpacePatch>,
310 replace: BTreeMap<String, TypeSpaceReplace>,
311 convert: Vec<TypeSpaceConversion>,
312}
313
314#[derive(Debug, Clone)]
315struct CrateSpec {
316 version: CrateVers,
317 rename: Option<String>,
318}
319
320#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Deserialize)]
323pub enum UnknownPolicy {
324 #[default]
326 Generate,
327 Allow,
333 Deny,
339}
340
341#[derive(Debug, Clone)]
344pub enum CrateVers {
345 Version(semver::Version),
347 Any,
349 Never,
351}
352
353impl CrateVers {
354 pub fn parse(s: &str) -> Option<Self> {
356 if s == "!" {
357 Some(Self::Never)
358 } else if s == "*" {
359 Some(Self::Any)
360 } else {
361 Some(Self::Version(semver::Version::parse(s).ok()?))
362 }
363 }
364}
365
366#[derive(Debug, Default, Clone)]
368pub struct TypeSpacePatch {
369 rename: Option<String>,
370 derives: Vec<String>,
371}
372
373#[derive(Debug, Default, Clone)]
375pub struct TypeSpaceReplace {
376 replace_type: String,
377 impls: Vec<TypeSpaceImpl>,
378}
379
380#[derive(Debug, Clone)]
383struct TypeSpaceConversion {
384 schema: schemars::schema::SchemaObject,
385 type_name: String,
386 impls: Vec<TypeSpaceImpl>,
387}
388
389#[allow(missing_docs)]
390#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
393#[non_exhaustive]
394pub enum TypeSpaceImpl {
395 FromStr,
396 Display,
397 Default,
398}
399
400impl std::str::FromStr for TypeSpaceImpl {
401 type Err = String;
402
403 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
404 match s {
405 "FromStr" => Ok(Self::FromStr),
406 "Display" => Ok(Self::Display),
407 "Default" => Ok(Self::Default),
408 _ => Err(format!("{} is not a valid trait specifier", s)),
409 }
410 }
411}
412
413impl TypeSpaceSettings {
414 pub fn with_type_mod<S: AsRef<str>>(&mut self, type_mod: S) -> &mut Self {
416 self.type_mod = Some(type_mod.as_ref().to_string());
417 self
418 }
419
420 pub fn with_derive(&mut self, derive: String) -> &mut Self {
422 if !self.extra_derives.contains(&derive) {
423 self.extra_derives.push(derive);
424 }
425 self
426 }
427
428 pub fn with_struct_builder(&mut self, struct_builder: bool) -> &mut Self {
430 self.struct_builder = struct_builder;
431 self
432 }
433
434 pub fn with_replacement<TS: ToString, RS: ToString, I: Iterator<Item = TypeSpaceImpl>>(
438 &mut self,
439 type_name: TS,
440 replace_type: RS,
441 impls: I,
442 ) -> &mut Self {
443 self.replace.insert(
444 type_name.to_string(),
445 TypeSpaceReplace {
446 replace_type: replace_type.to_string(),
447 impls: impls.collect(),
448 },
449 );
450 self
451 }
452
453 pub fn with_patch<S: ToString>(
458 &mut self,
459 type_name: S,
460 type_patch: &TypeSpacePatch,
461 ) -> &mut Self {
462 self.patch.insert(type_name.to_string(), type_patch.clone());
463 self
464 }
465
466 pub fn with_conversion<S: ToString, I: Iterator<Item = TypeSpaceImpl>>(
492 &mut self,
493 schema: schemars::schema::SchemaObject,
494 type_name: S,
495 impls: I,
496 ) -> &mut Self {
497 self.convert.push(TypeSpaceConversion {
498 schema,
499 type_name: type_name.to_string(),
500 impls: impls.collect(),
501 });
502 self
503 }
504
505 pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
510 self.unknown_crates = policy;
511 self
512 }
513
514 pub fn with_crate<S1: ToString>(
522 &mut self,
523 crate_name: S1,
524 version: CrateVers,
525 rename: Option<&String>,
526 ) -> &mut Self {
527 self.crates.insert(
528 crate_name.to_string(),
529 CrateSpec {
530 version,
531 rename: rename.cloned(),
532 },
533 );
534 self
535 }
536
537 pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
552 self.map_type = map_type.into();
553 self
554 }
555}
556
557impl TypeSpacePatch {
558 pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
560 self.rename = Some(rename.to_string());
561 self
562 }
563
564 pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
566 self.derives.push(derive.to_string());
567 self
568 }
569}
570
571impl TypeSpace {
572 pub fn new(settings: &TypeSpaceSettings) -> Self {
574 let mut cache = SchemaCache::default();
575
576 settings.convert.iter().for_each(
577 |TypeSpaceConversion {
578 schema,
579 type_name,
580 impls,
581 }| {
582 cache.insert(schema, type_name, impls);
583 },
584 );
585
586 Self {
587 settings: settings.clone(),
588 cache,
589 ..Default::default()
590 }
591 }
592
593 pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
603 where
604 I: IntoIterator<Item = (S, Schema)>,
605 S: AsRef<str>,
606 {
607 self.add_ref_types_impl(
608 type_defs
609 .into_iter()
610 .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
611 )
612 }
613
614 fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
615 where
616 I: IntoIterator<Item = (RefKey, Schema)>,
617 {
618 let definitions = type_defs.into_iter().collect::<Vec<_>>();
620
621 let base_id = self.next_id;
624 let def_len = definitions.len() as u64;
625 self.next_id += def_len;
626
627 for (index, (ref_name, schema)) in definitions.iter().enumerate() {
628 self.ref_to_id
629 .insert(ref_name.clone(), TypeId(base_id + index as u64));
630 self.definitions.insert(ref_name.clone(), schema.clone());
631 }
632
633 for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
638 info!(
639 "converting type: {:?} with schema {}",
640 ref_name,
641 serde_json::to_string(&schema).unwrap()
642 );
643
644 let type_id = TypeId(base_id + index as u64);
647
648 let maybe_replace = match &ref_name {
649 RefKey::Root => None,
650 RefKey::Def(def_name) => {
651 let check_name = sanitize(def_name, Case::Pascal);
652 self.settings.replace.get(&check_name)
653 }
654 };
655
656 match maybe_replace {
657 None => {
658 let type_name = if let RefKey::Def(name) = ref_name {
659 Name::Required(name.clone())
660 } else {
661 Name::Unknown
662 };
663 self.convert_ref_type(type_name, schema, type_id)?
664 }
665
666 Some(replace_type) => {
667 let type_entry = TypeEntry::new_native(
668 replace_type.replace_type.clone(),
669 &replace_type.impls.clone(),
670 );
671 self.id_to_entry.insert(type_id, type_entry);
672 }
673 }
674 }
675
676 self.break_cycles(base_id..base_id + def_len);
679
680 for index in base_id..self.next_id {
682 let type_id = TypeId(index);
683 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
684 type_entry.finalize(self)?;
685 self.id_to_entry.insert(type_id, type_entry);
686 }
687
688 Ok(())
689 }
690
691 fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
692 let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
693 let default = metadata
694 .as_ref()
695 .and_then(|m| m.default.as_ref())
696 .cloned()
697 .map(WrappedValue::new);
698 let type_entry = match &mut type_entry.details {
699 TypeEntryDetails::Enum(details) => {
701 details.default = default;
702 type_entry
703 }
704 TypeEntryDetails::Struct(details) => {
705 details.default = default;
706 type_entry
707 }
708 TypeEntryDetails::Newtype(details) => {
709 details.default = default;
710 type_entry
711 }
712
713 TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
718 self,
719 type_name,
720 metadata,
721 type_id.clone(),
722 schema.clone(),
723 ),
724
725 TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
726
727 _ => {
730 info!(
731 "type alias {:?} {}\n{:?}",
732 type_name,
733 serde_json::to_string_pretty(&schema).unwrap(),
734 metadata
735 );
736 let subtype_id = self.assign_type(type_entry);
737 TypeEntryNewtype::from_metadata(
738 self,
739 type_name,
740 metadata,
741 subtype_id,
742 schema.clone(),
743 )
744 }
745 };
746 if let Some(entry_name) = type_entry.name() {
748 self.name_to_id.insert(entry_name.clone(), type_id.clone());
749 }
750 self.id_to_entry.insert(type_id, type_entry);
751 Ok(())
752 }
753
754 pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
757 self.add_type_with_name(schema, None)
758 }
759
760 pub fn add_type_with_name(
763 &mut self,
764 schema: &Schema,
765 name_hint: Option<String>,
766 ) -> Result<TypeId> {
767 let base_id = self.next_id;
768
769 let name = match name_hint {
770 Some(s) => Name::Suggested(s),
771 None => Name::Unknown,
772 };
773 let (type_id, _) = self.id_for_schema(name, schema)?;
774
775 for index in base_id..self.next_id {
777 let type_id = TypeId(index);
778 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
779 type_entry.finalize(self)?;
780 self.id_to_entry.insert(type_id, type_entry);
781 }
782
783 Ok(type_id)
784 }
785
786 pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
790 let RootSchema {
791 meta_schema: _,
792 schema,
793 definitions,
794 } = schema;
795
796 let mut defs = definitions
797 .into_iter()
798 .map(|(key, schema)| (RefKey::Def(key), schema))
799 .collect::<Vec<_>>();
800
801 let root_type = schema
803 .metadata
804 .as_ref()
805 .and_then(|m| m.title.as_ref())
806 .is_some();
807
808 if root_type {
809 defs.push((RefKey::Root, schema.into()));
810 }
811
812 self.add_ref_types_impl(defs)?;
813
814 if root_type {
815 Ok(self.ref_to_id.get(&RefKey::Root).cloned())
816 } else {
817 Ok(None)
818 }
819 }
820
821 pub fn get_type(&self, type_id: &TypeId) -> Result<Type> {
823 let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
824 Ok(Type {
825 type_space: self,
826 type_entry,
827 })
828 }
829
830 pub fn uses_chrono(&self) -> bool {
832 self.uses_chrono
833 }
834
835 pub fn uses_regress(&self) -> bool {
837 self.uses_regress
838 }
839
840 pub fn uses_serde_json(&self) -> bool {
842 self.uses_serde_json
843 }
844
845 pub fn uses_uuid(&self) -> bool {
847 self.uses_uuid
848 }
849
850 pub fn iter_types(&self) -> impl Iterator<Item = Type> {
853 self.id_to_entry.values().map(move |type_entry| Type {
854 type_space: self,
855 type_entry,
856 })
857 }
858
859 pub fn to_stream(&self) -> TokenStream {
861 let mut output = OutputSpace::default();
862
863 output.add_item(
866 output::OutputSpaceMod::Error,
867 "",
868 quote! {
869 pub struct ConversionError(::std::borrow::Cow<'static, str>);
871
872 impl ::std::error::Error for ConversionError {}
873 impl ::std::fmt::Display for ConversionError {
874 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
875 -> Result<(), ::std::fmt::Error>
876 {
877 ::std::fmt::Display::fmt(&self.0, f)
878 }
879 }
880
881 impl ::std::fmt::Debug for ConversionError {
882 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
883 -> Result<(), ::std::fmt::Error>
884 {
885 ::std::fmt::Debug::fmt(&self.0, f)
886 }
887 }
888 impl From<&'static str> for ConversionError {
889 fn from(value: &'static str) -> Self {
890 Self(value.into())
891 }
892 }
893 impl From<String> for ConversionError {
894 fn from(value: String) -> Self {
895 Self(value.into())
896 }
897 }
898 },
899 );
900
901 self.id_to_entry
903 .values()
904 .for_each(|type_entry| type_entry.output(self, &mut output));
905
906 self.defaults
908 .iter()
909 .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
910
911 output.into_stream()
912 }
913
914 fn assign(&mut self) -> TypeId {
916 let id = TypeId(self.next_id);
917 self.next_id += 1;
918 id
919 }
920
921 fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
926 if let TypeEntryDetails::Reference(type_id) = ty.details {
927 type_id
928 } else if let Some(name) = ty.name() {
929 if let Some(type_id) = self.name_to_id.get(name) {
939 type_id.clone()
945 } else {
946 let type_id = self.assign();
947 self.name_to_id.insert(name.clone(), type_id.clone());
948 self.id_to_entry.insert(type_id.clone(), ty);
949 type_id
950 }
951 } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
952 type_id.clone()
953 } else {
954 let type_id = self.assign();
955 self.type_to_id.insert(ty.details.clone(), type_id.clone());
956 self.id_to_entry.insert(type_id.clone(), ty);
957 type_id
958 }
959 }
960
961 fn id_for_schema<'a>(
966 &mut self,
967 type_name: Name,
968 schema: &'a Schema,
969 ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
970 let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
971 if let Some(metadata) = metadata {
972 let default = metadata.default.clone().map(WrappedValue::new);
973 match &mut type_entry.details {
974 TypeEntryDetails::Enum(details) => {
975 details.default = default;
976 }
977 TypeEntryDetails::Struct(details) => {
978 details.default = default;
979 }
980 TypeEntryDetails::Newtype(details) => {
981 details.default = default;
982 }
983 _ => (),
984 }
985 }
986 let type_id = self.assign_type(type_entry);
987 Ok((type_id, metadata))
988 }
989
990 fn id_to_option(&mut self, id: &TypeId) -> TypeId {
992 self.assign_type(TypeEntryDetails::Option(id.clone()).into())
993 }
994
995 fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
997 TypeEntryDetails::Option(self.assign_type(ty)).into()
998 }
999
1000 fn id_to_box(&mut self, id: &TypeId) -> TypeId {
1002 self.assign_type(TypeEntryDetails::Box(id.clone()).into())
1003 }
1004}
1005
1006impl ToTokens for TypeSpace {
1007 fn to_tokens(&self, tokens: &mut TokenStream) {
1008 tokens.extend(self.to_stream())
1009 }
1010}
1011
1012impl<'a> Type<'a> {
1013 pub fn name(&self) -> String {
1015 let Type {
1016 type_space,
1017 type_entry,
1018 } = self;
1019 type_entry.type_name(type_space)
1020 }
1021
1022 pub fn ident(&self) -> TokenStream {
1025 let Type {
1026 type_space,
1027 type_entry,
1028 } = self;
1029 type_entry.type_ident(type_space, &type_space.settings.type_mod)
1030 }
1031
1032 pub fn parameter_ident(&self) -> TokenStream {
1036 let Type {
1037 type_space,
1038 type_entry,
1039 } = self;
1040 type_entry.type_parameter_ident(type_space, None)
1041 }
1042
1043 pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1048 let Type {
1049 type_space,
1050 type_entry,
1051 } = self;
1052 type_entry.type_parameter_ident(type_space, Some(lifetime))
1053 }
1054
1055 pub fn describe(&self) -> String {
1057 self.type_entry.describe()
1058 }
1059
1060 pub fn details(&self) -> TypeDetails {
1062 match &self.type_entry.details {
1063 TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1065 TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1066 TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1067
1068 TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1070 TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1071 TypeEntryDetails::Map(key_id, value_id) => {
1072 TypeDetails::Map(key_id.clone(), value_id.clone())
1073 }
1074 TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1075 TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1076 TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1077 TypeEntryDetails::Array(type_id, length) => {
1078 TypeDetails::Array(type_id.clone(), *length)
1079 }
1080
1081 TypeEntryDetails::Unit => TypeDetails::Unit,
1083 TypeEntryDetails::Native(TypeEntryNative {
1084 type_name: name, ..
1085 })
1086 | TypeEntryDetails::Integer(name)
1087 | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1088 TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1089 TypeEntryDetails::String => TypeDetails::String,
1090 TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1091
1092 TypeEntryDetails::Reference(_) => unreachable!(),
1094 }
1095 }
1096
1097 pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1099 let Type {
1100 type_space,
1101 type_entry,
1102 } = self;
1103 type_entry.has_impl(type_space, impl_name)
1104 }
1105
1106 pub fn builder(&self) -> Option<TokenStream> {
1108 let Type {
1109 type_space,
1110 type_entry,
1111 } = self;
1112
1113 if !type_space.settings.struct_builder {
1114 return None;
1115 }
1116
1117 match &type_entry.details {
1118 TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1119 match &type_space.settings.type_mod {
1120 Some(type_mod) => {
1121 let type_mod = format_ident!("{}", type_mod);
1122 let type_name = format_ident!("{}", name);
1123 Some(quote! { #type_mod :: builder :: #type_name })
1124 }
1125 None => {
1126 let type_name = format_ident!("{}", name);
1127 Some(quote! { builder :: #type_name })
1128 }
1129 }
1130 }
1131 _ => None,
1132 }
1133 }
1134}
1135
1136impl<'a> TypeEnum<'a> {
1137 pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1139 self.variants_info().map(|info| (info.name, info.details))
1140 }
1141
1142 pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1144 self.details.variants.iter().map(move |variant| {
1145 let details = match &variant.details {
1146 type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1147 type_entry::VariantDetails::Item(type_id) => {
1150 TypeEnumVariant::Tuple(vec![type_id.clone()])
1151 }
1152 type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1153 type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1154 properties
1155 .iter()
1156 .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1157 .collect(),
1158 ),
1159 };
1160 TypeEnumVariantInfo {
1161 name: variant.ident_name.as_ref().unwrap(),
1162 description: variant.description.as_deref(),
1163 details,
1164 }
1165 })
1166 }
1167}
1168
1169impl<'a> TypeStruct<'a> {
1170 pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1172 self.details
1173 .properties
1174 .iter()
1175 .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1176 }
1177
1178 pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo> {
1180 self.details
1181 .properties
1182 .iter()
1183 .map(move |prop| TypeStructPropInfo {
1184 name: prop.name.as_str(),
1185 description: prop.description.as_deref(),
1186 required: matches!(&prop.state, StructPropertyState::Required),
1187 type_id: prop.type_id.clone(),
1188 })
1189 }
1190}
1191
1192impl<'a> TypeNewtype<'a> {
1193 pub fn inner(&self) -> TypeId {
1195 self.details.type_id.clone()
1196 }
1197}
1198
1199#[cfg(test)]
1200mod tests {
1201 use schema::Schema;
1202 use schemars::{schema_for, JsonSchema};
1203 use serde::Serialize;
1204 use serde_json::json;
1205 use std::collections::HashSet;
1206
1207 use crate::{
1208 output::OutputSpace,
1209 test_util::validate_output,
1210 type_entry::{TypeEntryEnum, VariantDetails},
1211 Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1212 };
1213
1214 #[allow(dead_code)]
1215 #[derive(Serialize, JsonSchema)]
1216 struct Blah {
1217 blah: String,
1218 }
1219
1220 #[allow(dead_code)]
1221 #[derive(Serialize, JsonSchema)]
1222 #[serde(rename_all = "camelCase")]
1223 enum E {
1226 A,
1228 B,
1230 C(Blah),
1233 D {
1235 dd: String,
1237 },
1238 }
1246
1247 #[allow(dead_code)]
1248 #[derive(JsonSchema)]
1249 #[serde(rename_all = "camelCase")]
1250 struct Foo {
1251 #[serde(default)]
1253 bar: Option<String>,
1254 baz_baz: i32,
1255 e: E,
1257 }
1258
1259 #[test]
1260 fn test_simple() {
1261 let schema = schema_for!(Foo);
1262 println!("{:#?}", schema);
1263 let mut type_space = TypeSpace::default();
1264 type_space.add_ref_types(schema.definitions).unwrap();
1265 let (ty, _) = type_space
1266 .convert_schema_object(
1267 Name::Unknown,
1268 &schemars::schema::Schema::Object(schema.schema.clone()),
1269 &schema.schema,
1270 )
1271 .unwrap();
1272
1273 println!("{:#?}", ty);
1274
1275 let mut output = OutputSpace::default();
1276 ty.output(&type_space, &mut output);
1277 println!("{}", output.into_stream());
1278
1279 for ty in type_space.id_to_entry.values() {
1280 println!("{:#?}", ty);
1281 let mut output = OutputSpace::default();
1282 ty.output(&type_space, &mut output);
1283 println!("{}", output.into_stream());
1284 }
1285 }
1286
1287 #[test]
1288 fn test_external_references() {
1289 let schema = json!({
1290 "$schema": "http://json-schema.org/draft-04/schema#",
1291 "definitions": {
1292 "somename": {
1293 "$ref": "#/definitions/someothername",
1294 "required": [ "someproperty" ]
1295 },
1296 "someothername": {
1297 "type": "object",
1298 "properties": {
1299 "someproperty": {
1300 "type": "string"
1301 }
1302 }
1303 }
1304 }
1305 });
1306 let schema = serde_json::from_value(schema).unwrap();
1307 println!("{:#?}", schema);
1308 let settings = TypeSpaceSettings::default();
1309 let mut type_space = TypeSpace::new(&settings);
1310 type_space.add_root_schema(schema).unwrap();
1311 let tokens = type_space.to_stream().to_string();
1312 println!("{}", tokens);
1313 assert!(tokens
1314 .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1315 }
1316
1317 #[test]
1318 fn test_convert_enum_string() {
1319 #[allow(dead_code)]
1320 #[derive(JsonSchema)]
1321 #[serde(rename_all = "camelCase")]
1322 enum SimpleEnum {
1323 DotCom,
1324 Grizz,
1325 Kenneth,
1326 }
1327
1328 let schema = schema_for!(SimpleEnum);
1329 println!("{:#?}", schema);
1330
1331 let mut type_space = TypeSpace::default();
1332 type_space.add_ref_types(schema.definitions).unwrap();
1333 let (ty, _) = type_space
1334 .convert_schema_object(
1335 Name::Unknown,
1336 &schemars::schema::Schema::Object(schema.schema.clone()),
1337 &schema.schema,
1338 )
1339 .unwrap();
1340
1341 match ty.details {
1342 TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1343 for variant in &variants {
1344 assert_eq!(variant.details, VariantDetails::Simple);
1345 }
1346 let var_names = variants
1347 .iter()
1348 .map(|variant| variant.ident_name.as_ref().unwrap().clone())
1349 .collect::<HashSet<_>>();
1350 assert_eq!(
1351 var_names,
1352 ["DotCom", "Grizz", "Kenneth",]
1353 .iter()
1354 .map(ToString::to_string)
1355 .collect::<HashSet<_>>()
1356 );
1357 }
1358 _ => {
1359 let mut output = OutputSpace::default();
1360 ty.output(&type_space, &mut output);
1361 println!("{}", output.into_stream());
1362 panic!();
1363 }
1364 }
1365 }
1366
1367 #[test]
1368 fn test_string_enum_with_null() {
1369 let original_schema = json!({ "$ref": "xxx"});
1370 let enum_values = vec![
1371 json!("Shadrach"),
1372 json!("Meshach"),
1373 json!("Abednego"),
1374 json!(null),
1375 ];
1376
1377 let mut type_space = TypeSpace::default();
1378 let (te, _) = type_space
1379 .convert_enum_string(
1380 Name::Required("OnTheGo".to_string()),
1381 &serde_json::from_value(original_schema).unwrap(),
1382 &None,
1383 &enum_values,
1384 None,
1385 )
1386 .unwrap();
1387
1388 if let TypeEntryDetails::Option(id) = &te.details {
1389 let ote = type_space.id_to_entry.get(id).unwrap();
1390 if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1391 let variants = variants
1392 .iter()
1393 .map(|v| match v.details {
1394 VariantDetails::Simple => v.ident_name.as_ref().unwrap().clone(),
1395 _ => panic!("unexpected variant type"),
1396 })
1397 .collect::<HashSet<_>>();
1398
1399 assert_eq!(
1400 variants,
1401 enum_values
1402 .iter()
1403 .flat_map(|j| j.as_str().map(ToString::to_string))
1404 .collect::<HashSet<_>>()
1405 );
1406 } else {
1407 panic!("not the sub-type we expected {:#?}", te)
1408 }
1409 } else {
1410 panic!("not the type we expected {:#?}", te)
1411 }
1412 }
1413
1414 #[test]
1415 fn test_alias() {
1416 #[allow(dead_code)]
1417 #[derive(JsonSchema, Schema)]
1418 struct Stuff(Vec<String>);
1419
1420 #[allow(dead_code)]
1421 #[derive(JsonSchema, Schema)]
1422 struct Things {
1423 a: String,
1424 b: Stuff,
1425 }
1426
1427 validate_output::<Things>();
1428 }
1429
1430 #[test]
1431 fn test_builder_name() {
1432 #[allow(dead_code)]
1433 #[derive(JsonSchema)]
1434 struct TestStruct {
1435 x: u32,
1436 }
1437
1438 let mut type_space = TypeSpace::default();
1439 let schema = schema_for!(TestStruct);
1440 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1441 let ty = type_space.get_type(&type_id).unwrap();
1442
1443 assert!(ty.builder().is_none());
1444
1445 let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1446 let schema = schema_for!(TestStruct);
1447 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1448 let ty = type_space.get_type(&type_id).unwrap();
1449
1450 assert_eq!(
1451 ty.builder().map(|ts| ts.to_string()),
1452 Some("builder :: TestStruct".to_string())
1453 );
1454
1455 let mut type_space = TypeSpace::new(
1456 TypeSpaceSettings::default()
1457 .with_type_mod("types")
1458 .with_struct_builder(true),
1459 );
1460 let schema = schema_for!(TestStruct);
1461 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1462 let ty = type_space.get_type(&type_id).unwrap();
1463
1464 assert_eq!(
1465 ty.builder().map(|ts| ts.to_string()),
1466 Some("types :: builder :: TestStruct".to_string())
1467 );
1468
1469 #[allow(dead_code)]
1470 #[derive(JsonSchema)]
1471 enum TestEnum {
1472 X,
1473 Y,
1474 }
1475 let mut type_space = TypeSpace::new(
1476 TypeSpaceSettings::default()
1477 .with_type_mod("types")
1478 .with_struct_builder(true),
1479 );
1480 let schema = schema_for!(TestEnum);
1481 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1482 let ty = type_space.get_type(&type_id).unwrap();
1483 assert!(ty.builder().is_none());
1484 }
1485}