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 {
245 let map_type = syn::parse_str::<syn::Type>(s).expect("valid ident");
246 Self(map_type)
247 }
248}
249
250impl Default for MapType {
251 fn default() -> Self {
252 Self::new("::std::collections::HashMap")
253 }
254}
255
256impl std::fmt::Debug for MapType {
257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258 write!(f, "MapType({})", self.0.to_token_stream())
259 }
260}
261
262impl std::fmt::Display for MapType {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 self.0.to_token_stream().fmt(f)
265 }
266}
267
268impl<'de> serde::Deserialize<'de> for MapType {
269 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
270 where
271 D: serde::Deserializer<'de>,
272 {
273 let s = <&str>::deserialize(deserializer)?;
274 Ok(Self::new(s))
275 }
276}
277
278impl From<String> for MapType {
279 fn from(s: String) -> Self {
280 Self::new(&s)
281 }
282}
283
284impl From<&str> for MapType {
285 fn from(s: &str) -> Self {
286 Self::new(s)
287 }
288}
289
290impl From<syn::Type> for MapType {
291 fn from(t: syn::Type) -> Self {
292 Self(t)
293 }
294}
295
296#[derive(Default, Debug, Clone)]
298pub struct TypeSpaceSettings {
299 type_mod: Option<String>,
300 extra_derives: Vec<String>,
301 extra_attrs: Vec<String>,
302 struct_builder: bool,
303
304 unknown_crates: UnknownPolicy,
305 crates: BTreeMap<String, CrateSpec>,
306 map_type: MapType,
307
308 patch: BTreeMap<String, TypeSpacePatch>,
309 replace: BTreeMap<String, TypeSpaceReplace>,
310 convert: Vec<TypeSpaceConversion>,
311}
312
313#[derive(Debug, Clone)]
314struct CrateSpec {
315 version: CrateVers,
316 rename: Option<String>,
317}
318
319#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Deserialize)]
322pub enum UnknownPolicy {
323 #[default]
325 Generate,
326 Allow,
332 Deny,
338}
339
340#[derive(Debug, Clone)]
343pub enum CrateVers {
344 Version(semver::Version),
346 Any,
348 Never,
350}
351
352impl CrateVers {
353 pub fn parse(s: &str) -> Option<Self> {
355 if s == "!" {
356 Some(Self::Never)
357 } else if s == "*" {
358 Some(Self::Any)
359 } else {
360 Some(Self::Version(semver::Version::parse(s).ok()?))
361 }
362 }
363}
364
365#[derive(Debug, Default, Clone)]
367pub struct TypeSpacePatch {
368 rename: Option<String>,
369 derives: Vec<String>,
370 attrs: 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 FromStringIrrefutable,
397 Display,
398 Default,
399}
400
401impl std::str::FromStr for TypeSpaceImpl {
402 type Err = String;
403
404 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
405 match s {
406 "FromStr" => Ok(Self::FromStr),
407 "Display" => Ok(Self::Display),
408 "Default" => Ok(Self::Default),
409 _ => Err(format!("{} is not a valid trait specifier", s)),
410 }
411 }
412}
413
414impl TypeSpaceSettings {
415 pub fn with_type_mod<S: AsRef<str>>(&mut self, type_mod: S) -> &mut Self {
417 self.type_mod = Some(type_mod.as_ref().to_string());
418 self
419 }
420
421 pub fn with_derive(&mut self, derive: String) -> &mut Self {
423 if !self.extra_derives.contains(&derive) {
424 self.extra_derives.push(derive);
425 }
426 self
427 }
428
429 pub fn with_attr(&mut self, attr: String) -> &mut Self {
431 if !self.extra_attrs.contains(&attr) {
432 self.extra_attrs.push(attr);
433 }
434 self
435 }
436
437 pub fn with_struct_builder(&mut self, struct_builder: bool) -> &mut Self {
439 self.struct_builder = struct_builder;
440 self
441 }
442
443 pub fn with_replacement<TS: ToString, RS: ToString, I: Iterator<Item = TypeSpaceImpl>>(
447 &mut self,
448 type_name: TS,
449 replace_type: RS,
450 impls: I,
451 ) -> &mut Self {
452 self.replace.insert(
453 type_name.to_string(),
454 TypeSpaceReplace {
455 replace_type: replace_type.to_string(),
456 impls: impls.collect(),
457 },
458 );
459 self
460 }
461
462 pub fn with_patch<S: ToString>(
467 &mut self,
468 type_name: S,
469 type_patch: &TypeSpacePatch,
470 ) -> &mut Self {
471 self.patch.insert(type_name.to_string(), type_patch.clone());
472 self
473 }
474
475 pub fn with_conversion<S: ToString, I: Iterator<Item = TypeSpaceImpl>>(
501 &mut self,
502 schema: schemars::schema::SchemaObject,
503 type_name: S,
504 impls: I,
505 ) -> &mut Self {
506 self.convert.push(TypeSpaceConversion {
507 schema,
508 type_name: type_name.to_string(),
509 impls: impls.collect(),
510 });
511 self
512 }
513
514 pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
519 self.unknown_crates = policy;
520 self
521 }
522
523 pub fn with_crate<S1: ToString>(
531 &mut self,
532 crate_name: S1,
533 version: CrateVers,
534 rename: Option<&String>,
535 ) -> &mut Self {
536 self.crates.insert(
537 crate_name.to_string(),
538 CrateSpec {
539 version,
540 rename: rename.cloned(),
541 },
542 );
543 self
544 }
545
546 pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
561 self.map_type = map_type.into();
562 self
563 }
564}
565
566impl TypeSpacePatch {
567 pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
569 self.rename = Some(rename.to_string());
570 self
571 }
572
573 pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
575 self.derives.push(derive.to_string());
576 self
577 }
578
579 pub fn with_attr<S: ToString>(&mut self, attr: S) -> &mut Self {
581 self.attrs.push(attr.to_string());
582 self
583 }
584}
585
586impl TypeSpace {
587 pub fn new(settings: &TypeSpaceSettings) -> Self {
589 let mut cache = SchemaCache::default();
590
591 settings.convert.iter().for_each(
592 |TypeSpaceConversion {
593 schema,
594 type_name,
595 impls,
596 }| {
597 cache.insert(schema, type_name, impls);
598 },
599 );
600
601 Self {
602 settings: settings.clone(),
603 cache,
604 ..Default::default()
605 }
606 }
607
608 pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
618 where
619 I: IntoIterator<Item = (S, Schema)>,
620 S: AsRef<str>,
621 {
622 self.add_ref_types_impl(
623 type_defs
624 .into_iter()
625 .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
626 )
627 }
628
629 fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
630 where
631 I: IntoIterator<Item = (RefKey, Schema)>,
632 {
633 let definitions = type_defs.into_iter().collect::<Vec<_>>();
635
636 let base_id = self.next_id;
639 let def_len = definitions.len() as u64;
640 self.next_id += def_len;
641
642 for (index, (ref_name, schema)) in definitions.iter().enumerate() {
643 self.ref_to_id
644 .insert(ref_name.clone(), TypeId(base_id + index as u64));
645 self.definitions.insert(ref_name.clone(), schema.clone());
646 }
647
648 for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
653 info!(
654 "converting type: {:?} with schema {}",
655 ref_name,
656 serde_json::to_string(&schema).unwrap()
657 );
658
659 let type_id = TypeId(base_id + index as u64);
662
663 let maybe_replace = match &ref_name {
664 RefKey::Root => None,
665 RefKey::Def(def_name) => {
666 let check_name = sanitize(def_name, Case::Pascal);
667 self.settings.replace.get(&check_name)
668 }
669 };
670
671 match maybe_replace {
672 None => {
673 let type_name = if let RefKey::Def(name) = ref_name {
674 Name::Required(name.clone())
675 } else {
676 Name::Unknown
677 };
678 self.convert_ref_type(type_name, schema, type_id)?
679 }
680
681 Some(replace_type) => {
682 let type_entry = TypeEntry::new_native(
683 replace_type.replace_type.clone(),
684 &replace_type.impls.clone(),
685 );
686 self.id_to_entry.insert(type_id, type_entry);
687 }
688 }
689 }
690
691 self.break_cycles(base_id..base_id + def_len);
694
695 for index in base_id..self.next_id {
697 let type_id = TypeId(index);
698 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
699 debug!("finalizing type entry: {} {:#?}", index, &type_entry);
700 type_entry.finalize(self)?;
701 self.id_to_entry.insert(type_id, type_entry);
702 }
703
704 Ok(())
705 }
706
707 fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
708 let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
709 let default = metadata
710 .as_ref()
711 .and_then(|m| m.default.as_ref())
712 .cloned()
713 .map(WrappedValue::new);
714 let type_entry = match &mut type_entry.details {
715 TypeEntryDetails::Enum(details) => {
717 details.default = default;
718 type_entry
719 }
720 TypeEntryDetails::Struct(details) => {
721 details.default = default;
722 type_entry
723 }
724 TypeEntryDetails::Newtype(details) => {
725 details.default = default;
726 type_entry
727 }
728
729 TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
734 self,
735 type_name,
736 metadata,
737 type_id.clone(),
738 schema.clone(),
739 ),
740
741 TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
742
743 _ => {
746 info!(
747 "type alias {:?} {}\n{:?}",
748 type_name,
749 serde_json::to_string_pretty(&schema).unwrap(),
750 metadata
751 );
752 let subtype_id = self.assign_type(type_entry);
753 TypeEntryNewtype::from_metadata(
754 self,
755 type_name,
756 metadata,
757 subtype_id,
758 schema.clone(),
759 )
760 }
761 };
762 if let Some(entry_name) = type_entry.name() {
764 self.name_to_id.insert(entry_name.clone(), type_id.clone());
765 }
766 self.id_to_entry.insert(type_id, type_entry);
767 Ok(())
768 }
769
770 pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
773 self.add_type_with_name(schema, None)
774 }
775
776 pub fn add_type_with_name(
779 &mut self,
780 schema: &Schema,
781 name_hint: Option<String>,
782 ) -> Result<TypeId> {
783 let base_id = self.next_id;
784
785 let name = match name_hint {
786 Some(s) => Name::Suggested(s),
787 None => Name::Unknown,
788 };
789 let (type_id, _) = self.id_for_schema(name, schema)?;
790
791 for index in base_id..self.next_id {
793 let type_id = TypeId(index);
794 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
795 type_entry.finalize(self)?;
796 self.id_to_entry.insert(type_id, type_entry);
797 }
798
799 Ok(type_id)
800 }
801
802 pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
806 let RootSchema {
807 meta_schema: _,
808 schema,
809 definitions,
810 } = schema;
811
812 let mut defs = definitions
813 .into_iter()
814 .map(|(key, schema)| (RefKey::Def(key), schema))
815 .collect::<Vec<_>>();
816
817 let root_type = schema
819 .metadata
820 .as_ref()
821 .and_then(|m| m.title.as_ref())
822 .is_some();
823
824 if root_type {
825 defs.push((RefKey::Root, schema.into()));
826 }
827
828 self.add_ref_types_impl(defs)?;
829
830 if root_type {
831 Ok(self.ref_to_id.get(&RefKey::Root).cloned())
832 } else {
833 Ok(None)
834 }
835 }
836
837 pub fn get_type(&self, type_id: &TypeId) -> Result<Type<'_>> {
839 let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
840 Ok(Type {
841 type_space: self,
842 type_entry,
843 })
844 }
845
846 pub fn uses_chrono(&self) -> bool {
848 self.uses_chrono
849 }
850
851 pub fn uses_regress(&self) -> bool {
853 self.uses_regress
854 }
855
856 pub fn uses_serde_json(&self) -> bool {
858 self.uses_serde_json
859 }
860
861 pub fn uses_uuid(&self) -> bool {
863 self.uses_uuid
864 }
865
866 pub fn iter_types(&self) -> impl Iterator<Item = Type<'_>> {
869 self.id_to_entry.values().map(move |type_entry| Type {
870 type_space: self,
871 type_entry,
872 })
873 }
874
875 pub fn to_stream(&self) -> TokenStream {
877 let mut output = OutputSpace::default();
878
879 output.add_item(
882 output::OutputSpaceMod::Error,
883 "",
884 quote! {
885 pub struct ConversionError(::std::borrow::Cow<'static, str>);
887
888 impl ::std::error::Error for ConversionError {}
889 impl ::std::fmt::Display for ConversionError {
890 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
891 -> Result<(), ::std::fmt::Error>
892 {
893 ::std::fmt::Display::fmt(&self.0, f)
894 }
895 }
896
897 impl ::std::fmt::Debug for ConversionError {
898 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
899 -> Result<(), ::std::fmt::Error>
900 {
901 ::std::fmt::Debug::fmt(&self.0, f)
902 }
903 }
904 impl From<&'static str> for ConversionError {
905 fn from(value: &'static str) -> Self {
906 Self(value.into())
907 }
908 }
909 impl From<String> for ConversionError {
910 fn from(value: String) -> Self {
911 Self(value.into())
912 }
913 }
914 },
915 );
916
917 self.id_to_entry
919 .values()
920 .for_each(|type_entry| type_entry.output(self, &mut output));
921
922 self.defaults
924 .iter()
925 .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
926
927 output.into_stream()
928 }
929
930 fn assign(&mut self) -> TypeId {
932 let id = TypeId(self.next_id);
933 self.next_id += 1;
934 id
935 }
936
937 fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
942 if let TypeEntryDetails::Reference(type_id) = ty.details {
943 type_id
944 } else if let Some(name) = ty.name() {
945 if let Some(type_id) = self.name_to_id.get(name) {
955 type_id.clone()
961 } else {
962 let type_id = self.assign();
963 self.name_to_id.insert(name.clone(), type_id.clone());
964 self.id_to_entry.insert(type_id.clone(), ty);
965 type_id
966 }
967 } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
968 type_id.clone()
969 } else {
970 let type_id = self.assign();
971 self.type_to_id.insert(ty.details.clone(), type_id.clone());
972 self.id_to_entry.insert(type_id.clone(), ty);
973 type_id
974 }
975 }
976
977 fn id_for_schema<'a>(
982 &mut self,
983 type_name: Name,
984 schema: &'a Schema,
985 ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
986 let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
987 if let Some(metadata) = metadata {
988 let default = metadata.default.clone().map(WrappedValue::new);
989 match &mut type_entry.details {
990 TypeEntryDetails::Enum(details) => {
991 details.default = default;
992 }
993 TypeEntryDetails::Struct(details) => {
994 details.default = default;
995 }
996 TypeEntryDetails::Newtype(details) => {
997 details.default = default;
998 }
999 _ => (),
1000 }
1001 }
1002 let type_id = self.assign_type(type_entry);
1003 Ok((type_id, metadata))
1004 }
1005
1006 fn id_to_option(&mut self, id: &TypeId) -> TypeId {
1008 self.assign_type(TypeEntryDetails::Option(id.clone()).into())
1009 }
1010
1011 fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
1013 TypeEntryDetails::Option(self.assign_type(ty)).into()
1014 }
1015
1016 fn id_to_box(&mut self, id: &TypeId) -> TypeId {
1018 self.assign_type(TypeEntryDetails::Box(id.clone()).into())
1019 }
1020}
1021
1022impl ToTokens for TypeSpace {
1023 fn to_tokens(&self, tokens: &mut TokenStream) {
1024 tokens.extend(self.to_stream())
1025 }
1026}
1027
1028impl Type<'_> {
1029 pub fn name(&self) -> String {
1031 let Type {
1032 type_space,
1033 type_entry,
1034 } = self;
1035 type_entry.type_name(type_space)
1036 }
1037
1038 pub fn ident(&self) -> TokenStream {
1041 let Type {
1042 type_space,
1043 type_entry,
1044 } = self;
1045 type_entry.type_ident(type_space, &type_space.settings.type_mod)
1046 }
1047
1048 pub fn parameter_ident(&self) -> TokenStream {
1052 let Type {
1053 type_space,
1054 type_entry,
1055 } = self;
1056 type_entry.type_parameter_ident(type_space, None)
1057 }
1058
1059 pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1064 let Type {
1065 type_space,
1066 type_entry,
1067 } = self;
1068 type_entry.type_parameter_ident(type_space, Some(lifetime))
1069 }
1070
1071 pub fn describe(&self) -> String {
1073 self.type_entry.describe()
1074 }
1075
1076 pub fn details(&self) -> TypeDetails<'_> {
1078 match &self.type_entry.details {
1079 TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1081 TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1082 TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1083
1084 TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1086 TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1087 TypeEntryDetails::Map(key_id, value_id) => {
1088 TypeDetails::Map(key_id.clone(), value_id.clone())
1089 }
1090 TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1091 TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1092 TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1093 TypeEntryDetails::Array(type_id, length) => {
1094 TypeDetails::Array(type_id.clone(), *length)
1095 }
1096
1097 TypeEntryDetails::Unit => TypeDetails::Unit,
1099 TypeEntryDetails::Native(TypeEntryNative {
1100 type_name: name, ..
1101 })
1102 | TypeEntryDetails::Integer(name)
1103 | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1104 TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1105 TypeEntryDetails::String => TypeDetails::String,
1106 TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1107
1108 TypeEntryDetails::Reference(_) => unreachable!(),
1110 }
1111 }
1112
1113 pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1115 let Type {
1116 type_space,
1117 type_entry,
1118 } = self;
1119 type_entry.has_impl(type_space, impl_name)
1120 }
1121
1122 pub fn builder(&self) -> Option<TokenStream> {
1124 let Type {
1125 type_space,
1126 type_entry,
1127 } = self;
1128
1129 if !type_space.settings.struct_builder {
1130 return None;
1131 }
1132
1133 match &type_entry.details {
1134 TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1135 match &type_space.settings.type_mod {
1136 Some(type_mod) => {
1137 let type_mod = format_ident!("{}", type_mod);
1138 let type_name = format_ident!("{}", name);
1139 Some(quote! { #type_mod :: builder :: #type_name })
1140 }
1141 None => {
1142 let type_name = format_ident!("{}", name);
1143 Some(quote! { builder :: #type_name })
1144 }
1145 }
1146 }
1147 _ => None,
1148 }
1149 }
1150}
1151
1152impl<'a> TypeEnum<'a> {
1153 pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1155 self.variants_info().map(|info| (info.name, info.details))
1156 }
1157
1158 pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1160 self.details.variants.iter().map(move |variant| {
1161 let details = match &variant.details {
1162 type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1163 type_entry::VariantDetails::Item(type_id) => {
1166 TypeEnumVariant::Tuple(vec![type_id.clone()])
1167 }
1168 type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1169 type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1170 properties
1171 .iter()
1172 .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1173 .collect(),
1174 ),
1175 };
1176 TypeEnumVariantInfo {
1177 name: variant.ident_name.as_ref().unwrap(),
1178 description: variant.description.as_deref(),
1179 details,
1180 }
1181 })
1182 }
1183}
1184
1185impl<'a> TypeStruct<'a> {
1186 pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1188 self.details
1189 .properties
1190 .iter()
1191 .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1192 }
1193
1194 pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo<'a>> {
1196 self.details
1197 .properties
1198 .iter()
1199 .map(move |prop| TypeStructPropInfo {
1200 name: prop.name.as_str(),
1201 description: prop.description.as_deref(),
1202 required: matches!(&prop.state, StructPropertyState::Required),
1203 type_id: prop.type_id.clone(),
1204 })
1205 }
1206}
1207
1208impl TypeNewtype<'_> {
1209 pub fn inner(&self) -> TypeId {
1211 self.details.type_id.clone()
1212 }
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217 use schema::Schema;
1218 use schemars::{schema_for, JsonSchema};
1219 use serde::Serialize;
1220 use serde_json::json;
1221 use std::collections::HashSet;
1222
1223 use crate::{
1224 output::OutputSpace,
1225 test_util::validate_output,
1226 type_entry::{TypeEntryEnum, VariantDetails},
1227 Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1228 };
1229
1230 #[allow(dead_code)]
1231 #[derive(Serialize, JsonSchema)]
1232 struct Blah {
1233 blah: String,
1234 }
1235
1236 #[allow(dead_code)]
1237 #[derive(Serialize, JsonSchema)]
1238 #[serde(rename_all = "camelCase")]
1239 enum E {
1242 A,
1244 B,
1246 C(Blah),
1249 D {
1251 dd: String,
1253 },
1254 }
1262
1263 #[allow(dead_code)]
1264 #[derive(JsonSchema)]
1265 #[serde(rename_all = "camelCase")]
1266 struct Foo {
1267 #[serde(default)]
1269 bar: Option<String>,
1270 baz_baz: i32,
1271 e: E,
1273 }
1274
1275 #[test]
1276 fn test_simple() {
1277 let schema = schema_for!(Foo);
1278 println!("{:#?}", schema);
1279 let mut type_space = TypeSpace::default();
1280 type_space.add_ref_types(schema.definitions).unwrap();
1281 let (ty, _) = type_space
1282 .convert_schema_object(
1283 Name::Unknown,
1284 &schemars::schema::Schema::Object(schema.schema.clone()),
1285 &schema.schema,
1286 )
1287 .unwrap();
1288
1289 println!("{:#?}", ty);
1290
1291 let mut output = OutputSpace::default();
1292 ty.output(&type_space, &mut output);
1293 println!("{}", output.into_stream());
1294
1295 for ty in type_space.id_to_entry.values() {
1296 println!("{:#?}", ty);
1297 let mut output = OutputSpace::default();
1298 ty.output(&type_space, &mut output);
1299 println!("{}", output.into_stream());
1300 }
1301 }
1302
1303 #[test]
1304 fn test_external_references() {
1305 let schema = json!({
1306 "$schema": "http://json-schema.org/draft-04/schema#",
1307 "definitions": {
1308 "somename": {
1309 "$ref": "#/definitions/someothername",
1310 "required": [ "someproperty" ]
1311 },
1312 "someothername": {
1313 "type": "object",
1314 "properties": {
1315 "someproperty": {
1316 "type": "string"
1317 }
1318 }
1319 }
1320 }
1321 });
1322 let schema = serde_json::from_value(schema).unwrap();
1323 println!("{:#?}", schema);
1324 let settings = TypeSpaceSettings::default();
1325 let mut type_space = TypeSpace::new(&settings);
1326 type_space.add_root_schema(schema).unwrap();
1327 let tokens = type_space.to_stream().to_string();
1328 println!("{}", tokens);
1329 assert!(tokens
1330 .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1331 }
1332
1333 #[test]
1334 fn test_convert_enum_string() {
1335 #[allow(dead_code)]
1336 #[derive(JsonSchema)]
1337 #[serde(rename_all = "camelCase")]
1338 enum SimpleEnum {
1339 DotCom,
1340 Grizz,
1341 Kenneth,
1342 }
1343
1344 let schema = schema_for!(SimpleEnum);
1345 println!("{:#?}", schema);
1346
1347 let mut type_space = TypeSpace::default();
1348 type_space.add_ref_types(schema.definitions).unwrap();
1349 let (ty, _) = type_space
1350 .convert_schema_object(
1351 Name::Unknown,
1352 &schemars::schema::Schema::Object(schema.schema.clone()),
1353 &schema.schema,
1354 )
1355 .unwrap();
1356
1357 match ty.details {
1358 TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1359 for variant in &variants {
1360 assert_eq!(variant.details, VariantDetails::Simple);
1361 }
1362 let var_names = variants
1363 .iter()
1364 .map(|variant| variant.ident_name.as_ref().unwrap().clone())
1365 .collect::<HashSet<_>>();
1366 assert_eq!(
1367 var_names,
1368 ["DotCom", "Grizz", "Kenneth",]
1369 .iter()
1370 .map(ToString::to_string)
1371 .collect::<HashSet<_>>()
1372 );
1373 }
1374 _ => {
1375 let mut output = OutputSpace::default();
1376 ty.output(&type_space, &mut output);
1377 println!("{}", output.into_stream());
1378 panic!();
1379 }
1380 }
1381 }
1382
1383 #[test]
1384 fn test_string_enum_with_null() {
1385 let original_schema = json!({ "$ref": "xxx"});
1386 let enum_values = vec![
1387 json!("Shadrach"),
1388 json!("Meshach"),
1389 json!("Abednego"),
1390 json!(null),
1391 ];
1392
1393 let mut type_space = TypeSpace::default();
1394 let (te, _) = type_space
1395 .convert_enum_string(
1396 Name::Required("OnTheGo".to_string()),
1397 &serde_json::from_value(original_schema).unwrap(),
1398 &None,
1399 &enum_values,
1400 None,
1401 )
1402 .unwrap();
1403
1404 if let TypeEntryDetails::Option(id) = &te.details {
1405 let ote = type_space.id_to_entry.get(id).unwrap();
1406 if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1407 let variants = variants
1408 .iter()
1409 .map(|v| match v.details {
1410 VariantDetails::Simple => v.ident_name.as_ref().unwrap().clone(),
1411 _ => panic!("unexpected variant type"),
1412 })
1413 .collect::<HashSet<_>>();
1414
1415 assert_eq!(
1416 variants,
1417 enum_values
1418 .iter()
1419 .flat_map(|j| j.as_str().map(ToString::to_string))
1420 .collect::<HashSet<_>>()
1421 );
1422 } else {
1423 panic!("not the sub-type we expected {:#?}", te)
1424 }
1425 } else {
1426 panic!("not the type we expected {:#?}", te)
1427 }
1428 }
1429
1430 #[test]
1431 fn test_alias() {
1432 #[allow(dead_code)]
1433 #[derive(JsonSchema, Schema)]
1434 struct Stuff(Vec<String>);
1435
1436 #[allow(dead_code)]
1437 #[derive(JsonSchema, Schema)]
1438 struct Things {
1439 a: String,
1440 b: Stuff,
1441 }
1442
1443 validate_output::<Things>();
1444 }
1445
1446 #[test]
1447 fn test_builder_name() {
1448 #[allow(dead_code)]
1449 #[derive(JsonSchema)]
1450 struct TestStruct {
1451 x: u32,
1452 }
1453
1454 let mut type_space = TypeSpace::default();
1455 let schema = schema_for!(TestStruct);
1456 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1457 let ty = type_space.get_type(&type_id).unwrap();
1458
1459 assert!(ty.builder().is_none());
1460
1461 let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1462 let schema = schema_for!(TestStruct);
1463 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1464 let ty = type_space.get_type(&type_id).unwrap();
1465
1466 assert_eq!(
1467 ty.builder().map(|ts| ts.to_string()),
1468 Some("builder :: TestStruct".to_string())
1469 );
1470
1471 let mut type_space = TypeSpace::new(
1472 TypeSpaceSettings::default()
1473 .with_type_mod("types")
1474 .with_struct_builder(true),
1475 );
1476 let schema = schema_for!(TestStruct);
1477 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1478 let ty = type_space.get_type(&type_id).unwrap();
1479
1480 assert_eq!(
1481 ty.builder().map(|ts| ts.to_string()),
1482 Some("types :: builder :: TestStruct".to_string())
1483 );
1484
1485 #[allow(dead_code)]
1486 #[derive(JsonSchema)]
1487 enum TestEnum {
1488 X,
1489 Y,
1490 }
1491 let mut type_space = TypeSpace::new(
1492 TypeSpaceSettings::default()
1493 .with_type_mod("types")
1494 .with_struct_builder(true),
1495 );
1496 let schema = schema_for!(TestEnum);
1497 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1498 let ty = type_space.get_type(&type_id).unwrap();
1499 assert!(ty.builder().is_none());
1500 }
1501}