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>>(
472 &mut self,
473 schema: schemars::schema::SchemaObject,
474 type_name: S,
475 impls: I,
476 ) -> &mut Self {
477 self.convert.push(TypeSpaceConversion {
478 schema,
479 type_name: type_name.to_string(),
480 impls: impls.collect(),
481 });
482 self
483 }
484
485 pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
490 self.unknown_crates = policy;
491 self
492 }
493
494 pub fn with_crate<S1: ToString>(
502 &mut self,
503 crate_name: S1,
504 version: CrateVers,
505 rename: Option<&String>,
506 ) -> &mut Self {
507 self.crates.insert(
508 crate_name.to_string(),
509 CrateSpec {
510 version,
511 rename: rename.cloned(),
512 },
513 );
514 self
515 }
516
517 pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
532 self.map_type = map_type.into();
533 self
534 }
535}
536
537impl TypeSpacePatch {
538 pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
540 self.rename = Some(rename.to_string());
541 self
542 }
543
544 pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
546 self.derives.push(derive.to_string());
547 self
548 }
549}
550
551impl TypeSpace {
552 pub fn new(settings: &TypeSpaceSettings) -> Self {
554 let mut cache = SchemaCache::default();
555
556 settings.convert.iter().for_each(
557 |TypeSpaceConversion {
558 schema,
559 type_name,
560 impls,
561 }| {
562 cache.insert(schema, type_name, impls);
563 },
564 );
565
566 Self {
567 settings: settings.clone(),
568 cache,
569 ..Default::default()
570 }
571 }
572
573 pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
583 where
584 I: IntoIterator<Item = (S, Schema)>,
585 S: AsRef<str>,
586 {
587 self.add_ref_types_impl(
588 type_defs
589 .into_iter()
590 .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
591 )
592 }
593
594 fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
595 where
596 I: IntoIterator<Item = (RefKey, Schema)>,
597 {
598 let definitions = type_defs.into_iter().collect::<Vec<_>>();
600
601 let base_id = self.next_id;
604 let def_len = definitions.len() as u64;
605 self.next_id += def_len;
606
607 for (index, (ref_name, schema)) in definitions.iter().enumerate() {
608 self.ref_to_id
609 .insert(ref_name.clone(), TypeId(base_id + index as u64));
610 self.definitions.insert(ref_name.clone(), schema.clone());
611 }
612
613 for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
618 info!(
619 "converting type: {:?} with schema {}",
620 ref_name,
621 serde_json::to_string(&schema).unwrap()
622 );
623
624 let type_id = TypeId(base_id + index as u64);
627
628 let maybe_replace = match &ref_name {
629 RefKey::Root => None,
630 RefKey::Def(def_name) => {
631 let check_name = sanitize(def_name, Case::Pascal);
632 self.settings.replace.get(&check_name)
633 }
634 };
635
636 match maybe_replace {
637 None => {
638 let type_name = if let RefKey::Def(name) = ref_name {
639 Name::Required(name.clone())
640 } else {
641 Name::Unknown
642 };
643 self.convert_ref_type(type_name, schema, type_id)?
644 }
645
646 Some(replace_type) => {
647 let type_entry = TypeEntry::new_native(
648 replace_type.replace_type.clone(),
649 &replace_type.impls.clone(),
650 );
651 self.id_to_entry.insert(type_id, type_entry);
652 }
653 }
654 }
655
656 self.break_cycles(base_id..base_id + def_len);
659
660 for index in base_id..self.next_id {
662 let type_id = TypeId(index);
663 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
664 type_entry.finalize(self)?;
665 self.id_to_entry.insert(type_id, type_entry);
666 }
667
668 Ok(())
669 }
670
671 fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
672 let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
673 let default = metadata
674 .as_ref()
675 .and_then(|m| m.default.as_ref())
676 .cloned()
677 .map(WrappedValue::new);
678 let type_entry = match &mut type_entry.details {
679 TypeEntryDetails::Enum(details) => {
681 details.default = default;
682 type_entry
683 }
684 TypeEntryDetails::Struct(details) => {
685 details.default = default;
686 type_entry
687 }
688 TypeEntryDetails::Newtype(details) => {
689 details.default = default;
690 type_entry
691 }
692
693 TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
698 self,
699 type_name,
700 metadata,
701 type_id.clone(),
702 schema.clone(),
703 ),
704
705 TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
706
707 _ => {
710 info!(
711 "type alias {:?} {}\n{:?}",
712 type_name,
713 serde_json::to_string_pretty(&schema).unwrap(),
714 metadata
715 );
716 let subtype_id = self.assign_type(type_entry);
717 TypeEntryNewtype::from_metadata(
718 self,
719 type_name,
720 metadata,
721 subtype_id,
722 schema.clone(),
723 )
724 }
725 };
726 if let Some(entry_name) = type_entry.name() {
728 self.name_to_id.insert(entry_name.clone(), type_id.clone());
729 }
730 self.id_to_entry.insert(type_id, type_entry);
731 Ok(())
732 }
733
734 pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
737 self.add_type_with_name(schema, None)
738 }
739
740 pub fn add_type_with_name(
743 &mut self,
744 schema: &Schema,
745 name_hint: Option<String>,
746 ) -> Result<TypeId> {
747 let base_id = self.next_id;
748
749 let name = match name_hint {
750 Some(s) => Name::Suggested(s),
751 None => Name::Unknown,
752 };
753 let (type_id, _) = self.id_for_schema(name, schema)?;
754
755 for index in base_id..self.next_id {
757 let type_id = TypeId(index);
758 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
759 type_entry.finalize(self)?;
760 self.id_to_entry.insert(type_id, type_entry);
761 }
762
763 Ok(type_id)
764 }
765
766 pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
770 let RootSchema {
771 meta_schema: _,
772 schema,
773 definitions,
774 } = schema;
775
776 let mut defs = definitions
777 .into_iter()
778 .map(|(key, schema)| (RefKey::Def(key), schema))
779 .collect::<Vec<_>>();
780
781 let root_type = schema
783 .metadata
784 .as_ref()
785 .and_then(|m| m.title.as_ref())
786 .is_some();
787
788 if root_type {
789 defs.push((RefKey::Root, schema.into()));
790 }
791
792 self.add_ref_types_impl(defs)?;
793
794 if root_type {
795 Ok(self.ref_to_id.get(&RefKey::Root).cloned())
796 } else {
797 Ok(None)
798 }
799 }
800
801 pub fn get_type(&self, type_id: &TypeId) -> Result<Type> {
803 let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
804 Ok(Type {
805 type_space: self,
806 type_entry,
807 })
808 }
809
810 pub fn uses_chrono(&self) -> bool {
812 self.uses_chrono
813 }
814
815 pub fn uses_regress(&self) -> bool {
817 self.uses_regress
818 }
819
820 pub fn uses_serde_json(&self) -> bool {
822 self.uses_serde_json
823 }
824
825 pub fn uses_uuid(&self) -> bool {
827 self.uses_uuid
828 }
829
830 pub fn iter_types(&self) -> impl Iterator<Item = Type> {
833 self.id_to_entry.values().map(move |type_entry| Type {
834 type_space: self,
835 type_entry,
836 })
837 }
838
839 pub fn to_stream(&self) -> TokenStream {
841 let mut output = OutputSpace::default();
842
843 output.add_item(
846 output::OutputSpaceMod::Error,
847 "",
848 quote! {
849 pub struct ConversionError(::std::borrow::Cow<'static, str>);
851
852 impl ::std::error::Error for ConversionError {}
853 impl ::std::fmt::Display for ConversionError {
854 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
855 -> Result<(), ::std::fmt::Error>
856 {
857 ::std::fmt::Display::fmt(&self.0, f)
858 }
859 }
860
861 impl ::std::fmt::Debug for ConversionError {
862 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
863 -> Result<(), ::std::fmt::Error>
864 {
865 ::std::fmt::Debug::fmt(&self.0, f)
866 }
867 }
868 impl From<&'static str> for ConversionError {
869 fn from(value: &'static str) -> Self {
870 Self(value.into())
871 }
872 }
873 impl From<String> for ConversionError {
874 fn from(value: String) -> Self {
875 Self(value.into())
876 }
877 }
878 },
879 );
880
881 self.id_to_entry
883 .values()
884 .for_each(|type_entry| type_entry.output(self, &mut output));
885
886 self.defaults
888 .iter()
889 .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
890
891 output.into_stream()
892 }
893
894 fn assign(&mut self) -> TypeId {
896 let id = TypeId(self.next_id);
897 self.next_id += 1;
898 id
899 }
900
901 fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
906 if let TypeEntryDetails::Reference(type_id) = ty.details {
907 type_id
908 } else if let Some(name) = ty.name() {
909 if let Some(type_id) = self.name_to_id.get(name) {
919 type_id.clone()
925 } else {
926 let type_id = self.assign();
927 self.name_to_id.insert(name.clone(), type_id.clone());
928 self.id_to_entry.insert(type_id.clone(), ty);
929 type_id
930 }
931 } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
932 type_id.clone()
933 } else {
934 let type_id = self.assign();
935 self.type_to_id.insert(ty.details.clone(), type_id.clone());
936 self.id_to_entry.insert(type_id.clone(), ty);
937 type_id
938 }
939 }
940
941 fn id_for_schema<'a>(
946 &mut self,
947 type_name: Name,
948 schema: &'a Schema,
949 ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
950 let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
951 if let Some(metadata) = metadata {
952 let default = metadata.default.clone().map(WrappedValue::new);
953 match &mut type_entry.details {
954 TypeEntryDetails::Enum(details) => {
955 details.default = default;
956 }
957 TypeEntryDetails::Struct(details) => {
958 details.default = default;
959 }
960 TypeEntryDetails::Newtype(details) => {
961 details.default = default;
962 }
963 _ => (),
964 }
965 }
966 let type_id = self.assign_type(type_entry);
967 Ok((type_id, metadata))
968 }
969
970 fn id_to_option(&mut self, id: &TypeId) -> TypeId {
972 self.assign_type(TypeEntryDetails::Option(id.clone()).into())
973 }
974
975 fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
977 TypeEntryDetails::Option(self.assign_type(ty)).into()
978 }
979
980 fn id_to_box(&mut self, id: &TypeId) -> TypeId {
982 self.assign_type(TypeEntryDetails::Box(id.clone()).into())
983 }
984}
985
986impl ToTokens for TypeSpace {
987 fn to_tokens(&self, tokens: &mut TokenStream) {
988 tokens.extend(self.to_stream())
989 }
990}
991
992impl<'a> Type<'a> {
993 pub fn name(&self) -> String {
995 let Type {
996 type_space,
997 type_entry,
998 } = self;
999 type_entry.type_name(type_space)
1000 }
1001
1002 pub fn ident(&self) -> TokenStream {
1005 let Type {
1006 type_space,
1007 type_entry,
1008 } = self;
1009 type_entry.type_ident(type_space, &type_space.settings.type_mod)
1010 }
1011
1012 pub fn parameter_ident(&self) -> TokenStream {
1016 let Type {
1017 type_space,
1018 type_entry,
1019 } = self;
1020 type_entry.type_parameter_ident(type_space, None)
1021 }
1022
1023 pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1028 let Type {
1029 type_space,
1030 type_entry,
1031 } = self;
1032 type_entry.type_parameter_ident(type_space, Some(lifetime))
1033 }
1034
1035 pub fn describe(&self) -> String {
1037 self.type_entry.describe()
1038 }
1039
1040 pub fn details(&self) -> TypeDetails {
1042 match &self.type_entry.details {
1043 TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1045 TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1046 TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1047
1048 TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1050 TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1051 TypeEntryDetails::Map(key_id, value_id) => {
1052 TypeDetails::Map(key_id.clone(), value_id.clone())
1053 }
1054 TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1055 TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1056 TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1057 TypeEntryDetails::Array(type_id, length) => {
1058 TypeDetails::Array(type_id.clone(), *length)
1059 }
1060
1061 TypeEntryDetails::Unit => TypeDetails::Unit,
1063 TypeEntryDetails::Native(TypeEntryNative {
1064 type_name: name, ..
1065 })
1066 | TypeEntryDetails::Integer(name)
1067 | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1068 TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1069 TypeEntryDetails::String => TypeDetails::String,
1070 TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1071
1072 TypeEntryDetails::Reference(_) => unreachable!(),
1074 }
1075 }
1076
1077 pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1079 let Type {
1080 type_space,
1081 type_entry,
1082 } = self;
1083 type_entry.has_impl(type_space, impl_name)
1084 }
1085
1086 pub fn builder(&self) -> Option<TokenStream> {
1088 let Type {
1089 type_space,
1090 type_entry,
1091 } = self;
1092
1093 if !type_space.settings.struct_builder {
1094 return None;
1095 }
1096
1097 match &type_entry.details {
1098 TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1099 match &type_space.settings.type_mod {
1100 Some(type_mod) => {
1101 let type_mod = format_ident!("{}", type_mod);
1102 let type_name = format_ident!("{}", name);
1103 Some(quote! { #type_mod :: builder :: #type_name })
1104 }
1105 None => {
1106 let type_name = format_ident!("{}", name);
1107 Some(quote! { builder :: #type_name })
1108 }
1109 }
1110 }
1111 _ => None,
1112 }
1113 }
1114}
1115
1116impl<'a> TypeEnum<'a> {
1117 pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1119 self.variants_info().map(|info| (info.name, info.details))
1120 }
1121
1122 pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1124 self.details.variants.iter().map(move |variant| {
1125 let details = match &variant.details {
1126 type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1127 type_entry::VariantDetails::Item(type_id) => {
1130 TypeEnumVariant::Tuple(vec![type_id.clone()])
1131 }
1132 type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1133 type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1134 properties
1135 .iter()
1136 .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1137 .collect(),
1138 ),
1139 };
1140 TypeEnumVariantInfo {
1141 name: variant.name.as_str(),
1142 description: variant.description.as_deref(),
1143 details,
1144 }
1145 })
1146 }
1147}
1148
1149impl<'a> TypeStruct<'a> {
1150 pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1152 self.details
1153 .properties
1154 .iter()
1155 .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1156 }
1157
1158 pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo> {
1160 self.details
1161 .properties
1162 .iter()
1163 .map(move |prop| TypeStructPropInfo {
1164 name: prop.name.as_str(),
1165 description: prop.description.as_deref(),
1166 required: matches!(&prop.state, StructPropertyState::Required),
1167 type_id: prop.type_id.clone(),
1168 })
1169 }
1170}
1171
1172impl<'a> TypeNewtype<'a> {
1173 pub fn inner(&self) -> TypeId {
1175 self.details.type_id.clone()
1176 }
1177}
1178
1179#[cfg(test)]
1180mod tests {
1181 use schema::Schema;
1182 use schemars::{schema_for, JsonSchema};
1183 use serde::Serialize;
1184 use serde_json::json;
1185 use std::collections::HashSet;
1186
1187 use crate::{
1188 output::OutputSpace,
1189 test_util::validate_output,
1190 type_entry::{TypeEntryEnum, VariantDetails},
1191 Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1192 };
1193
1194 #[allow(dead_code)]
1195 #[derive(Serialize, JsonSchema)]
1196 struct Blah {
1197 blah: String,
1198 }
1199
1200 #[allow(dead_code)]
1201 #[derive(Serialize, JsonSchema)]
1202 #[serde(rename_all = "camelCase")]
1203 enum E {
1206 A,
1208 B,
1210 C(Blah),
1213 D {
1215 dd: String,
1217 },
1218 }
1226
1227 #[allow(dead_code)]
1228 #[derive(JsonSchema)]
1229 #[serde(rename_all = "camelCase")]
1230 struct Foo {
1231 #[serde(default)]
1233 bar: Option<String>,
1234 baz_baz: i32,
1235 e: E,
1237 }
1238
1239 #[test]
1240 fn test_simple() {
1241 let schema = schema_for!(Foo);
1242 println!("{:#?}", schema);
1243 let mut type_space = TypeSpace::default();
1244 type_space.add_ref_types(schema.definitions).unwrap();
1245 let (ty, _) = type_space
1246 .convert_schema_object(
1247 Name::Unknown,
1248 &schemars::schema::Schema::Object(schema.schema.clone()),
1249 &schema.schema,
1250 )
1251 .unwrap();
1252
1253 println!("{:#?}", ty);
1254
1255 let mut output = OutputSpace::default();
1256 ty.output(&type_space, &mut output);
1257 println!("{}", output.into_stream());
1258
1259 for ty in type_space.id_to_entry.values() {
1260 println!("{:#?}", ty);
1261 let mut output = OutputSpace::default();
1262 ty.output(&type_space, &mut output);
1263 println!("{}", output.into_stream());
1264 }
1265 }
1266
1267 #[test]
1268 fn test_external_references() {
1269 let schema = json!({
1270 "$schema": "http://json-schema.org/draft-04/schema#",
1271 "definitions": {
1272 "somename": {
1273 "$ref": "#/definitions/someothername",
1274 "required": [ "someproperty" ]
1275 },
1276 "someothername": {
1277 "type": "object",
1278 "properties": {
1279 "someproperty": {
1280 "type": "string"
1281 }
1282 }
1283 }
1284 }
1285 });
1286 let schema = serde_json::from_value(schema).unwrap();
1287 println!("{:#?}", schema);
1288 let settings = TypeSpaceSettings::default();
1289 let mut type_space = TypeSpace::new(&settings);
1290 type_space.add_root_schema(schema).unwrap();
1291 let tokens = type_space.to_stream().to_string();
1292 println!("{}", tokens);
1293 assert!(tokens
1294 .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1295 }
1296
1297 #[test]
1298 fn test_convert_enum_string() {
1299 #[allow(dead_code)]
1300 #[derive(JsonSchema)]
1301 #[serde(rename_all = "camelCase")]
1302 enum SimpleEnum {
1303 DotCom,
1304 Grizz,
1305 Kenneth,
1306 }
1307
1308 let schema = schema_for!(SimpleEnum);
1309 println!("{:#?}", schema);
1310
1311 let mut type_space = TypeSpace::default();
1312 type_space.add_ref_types(schema.definitions).unwrap();
1313 let (ty, _) = type_space
1314 .convert_schema_object(
1315 Name::Unknown,
1316 &schemars::schema::Schema::Object(schema.schema.clone()),
1317 &schema.schema,
1318 )
1319 .unwrap();
1320
1321 match ty.details {
1322 TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1323 for variant in &variants {
1324 assert_eq!(variant.details, VariantDetails::Simple);
1325 }
1326 let var_names = variants
1327 .iter()
1328 .map(|variant| variant.name.clone())
1329 .collect::<HashSet<_>>();
1330 assert_eq!(
1331 var_names,
1332 ["DotCom", "Grizz", "Kenneth",]
1333 .iter()
1334 .map(ToString::to_string)
1335 .collect::<HashSet<_>>()
1336 );
1337 }
1338 _ => {
1339 let mut output = OutputSpace::default();
1340 ty.output(&type_space, &mut output);
1341 println!("{}", output.into_stream());
1342 panic!();
1343 }
1344 }
1345 }
1346
1347 #[test]
1348 fn test_string_enum_with_null() {
1349 let original_schema = json!({ "$ref": "xxx"});
1350 let enum_values = vec![
1351 json!("Shadrach"),
1352 json!("Meshach"),
1353 json!("Abednego"),
1354 json!(null),
1355 ];
1356
1357 let mut type_space = TypeSpace::default();
1358 let (te, _) = type_space
1359 .convert_enum_string(
1360 Name::Required("OnTheGo".to_string()),
1361 &serde_json::from_value(original_schema).unwrap(),
1362 &None,
1363 &enum_values,
1364 None,
1365 )
1366 .unwrap();
1367
1368 if let TypeEntryDetails::Option(id) = &te.details {
1369 let ote = type_space.id_to_entry.get(id).unwrap();
1370 if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1371 let variants = variants
1372 .iter()
1373 .map(|v| match v.details {
1374 VariantDetails::Simple => v.name.clone(),
1375 _ => panic!("unexpected variant type"),
1376 })
1377 .collect::<HashSet<_>>();
1378
1379 assert_eq!(
1380 variants,
1381 enum_values
1382 .iter()
1383 .flat_map(|j| j.as_str().map(ToString::to_string))
1384 .collect::<HashSet<_>>()
1385 );
1386 } else {
1387 panic!("not the sub-type we expected {:#?}", te)
1388 }
1389 } else {
1390 panic!("not the type we expected {:#?}", te)
1391 }
1392 }
1393
1394 #[test]
1395 fn test_alias() {
1396 #[allow(dead_code)]
1397 #[derive(JsonSchema, Schema)]
1398 struct Stuff(Vec<String>);
1399
1400 #[allow(dead_code)]
1401 #[derive(JsonSchema, Schema)]
1402 struct Things {
1403 a: String,
1404 b: Stuff,
1405 }
1406
1407 validate_output::<Things>();
1408 }
1409
1410 #[test]
1411 fn test_builder_name() {
1412 #[allow(dead_code)]
1413 #[derive(JsonSchema)]
1414 struct TestStruct {
1415 x: u32,
1416 }
1417
1418 let mut type_space = TypeSpace::default();
1419 let schema = schema_for!(TestStruct);
1420 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1421 let ty = type_space.get_type(&type_id).unwrap();
1422
1423 assert!(ty.builder().is_none());
1424
1425 let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1426 let schema = schema_for!(TestStruct);
1427 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1428 let ty = type_space.get_type(&type_id).unwrap();
1429
1430 assert_eq!(
1431 ty.builder().map(|ts| ts.to_string()),
1432 Some("builder :: TestStruct".to_string())
1433 );
1434
1435 let mut type_space = TypeSpace::new(
1436 TypeSpaceSettings::default()
1437 .with_type_mod("types")
1438 .with_struct_builder(true),
1439 );
1440 let schema = schema_for!(TestStruct);
1441 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1442 let ty = type_space.get_type(&type_id).unwrap();
1443
1444 assert_eq!(
1445 ty.builder().map(|ts| ts.to_string()),
1446 Some("types :: builder :: TestStruct".to_string())
1447 );
1448
1449 #[allow(dead_code)]
1450 #[derive(JsonSchema)]
1451 enum TestEnum {
1452 X,
1453 Y,
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!(TestEnum);
1461 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1462 let ty = type_space.get_type(&type_id).unwrap();
1463 assert!(ty.builder().is_none());
1464 }
1465}