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 type_name.unwrap_or("<unknown type>")
67}
68
69#[derive(Debug)]
71pub struct Type<'a> {
72 type_space: &'a TypeSpace,
73 type_entry: &'a TypeEntry,
74}
75
76#[allow(missing_docs)]
77pub enum TypeDetails<'a> {
79 Enum(TypeEnum<'a>),
80 Struct(TypeStruct<'a>),
81 Newtype(TypeNewtype<'a>),
82
83 Option(TypeId),
84 Vec(TypeId),
85 Map(TypeId, TypeId),
86 Set(TypeId),
87 Box(TypeId),
88 Tuple(Box<dyn Iterator<Item = TypeId> + 'a>),
89 Array(TypeId, usize),
90 Builtin(&'a str),
91
92 Unit,
93 String,
94}
95
96pub struct TypeEnum<'a> {
98 details: &'a type_entry::TypeEntryEnum,
99}
100
101pub enum TypeEnumVariant<'a> {
103 Simple,
105 Tuple(Vec<TypeId>),
107 Struct(Vec<(&'a str, TypeId)>),
109}
110
111pub struct TypeEnumVariantInfo<'a> {
113 pub name: &'a str,
115 pub description: Option<&'a str>,
117 pub details: TypeEnumVariant<'a>,
119}
120
121pub struct TypeStruct<'a> {
123 details: &'a type_entry::TypeEntryStruct,
124}
125
126pub struct TypeStructPropInfo<'a> {
128 pub name: &'a str,
130 pub description: Option<&'a str>,
132 pub required: bool,
134 pub type_id: TypeId,
136}
137
138pub struct TypeNewtype<'a> {
140 details: &'a type_entry::TypeEntryNewtype,
141}
142
143#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Hash)]
145pub struct TypeId(u64);
146
147#[derive(Debug, Clone, PartialEq)]
148pub(crate) enum Name {
149 Required(String),
150 Suggested(String),
151 Unknown,
152}
153
154impl Name {
155 pub fn into_option(self) -> Option<String> {
156 match self {
157 Name::Required(s) | Name::Suggested(s) => Some(s),
158 Name::Unknown => None,
159 }
160 }
161
162 pub fn append(&self, s: &str) -> Self {
163 match self {
164 Name::Required(prefix) | Name::Suggested(prefix) => {
165 Self::Suggested(format!("{}_{}", prefix, s))
166 }
167 Name::Unknown => Name::Unknown,
168 }
169 }
170}
171
172#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
173pub(crate) enum RefKey {
174 Root,
175 Def(String),
176}
177
178#[derive(Debug)]
180pub struct TypeSpace {
181 next_id: u64,
182
183 definitions: BTreeMap<RefKey, Schema>,
188
189 id_to_entry: BTreeMap<TypeId, TypeEntry>,
190 type_to_id: BTreeMap<TypeEntryDetails, TypeId>,
191
192 name_to_id: BTreeMap<String, TypeId>,
193 ref_to_id: BTreeMap<RefKey, TypeId>,
194
195 uses_chrono: bool,
196 uses_uuid: bool,
197 uses_serde_json: bool,
198 uses_regress: bool,
199
200 settings: TypeSpaceSettings,
201
202 cache: SchemaCache,
203
204 defaults: BTreeSet<DefaultImpl>,
206}
207
208impl Default for TypeSpace {
209 fn default() -> Self {
210 Self {
211 next_id: 1,
212 definitions: Default::default(),
213 id_to_entry: Default::default(),
214 type_to_id: Default::default(),
215 name_to_id: Default::default(),
216 ref_to_id: Default::default(),
217 uses_chrono: Default::default(),
218 uses_uuid: Default::default(),
219 uses_serde_json: Default::default(),
220 uses_regress: Default::default(),
221 settings: Default::default(),
222 cache: Default::default(),
223 defaults: Default::default(),
224 }
225 }
226}
227
228#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
229pub(crate) enum DefaultImpl {
230 Boolean,
231 I64,
232 U64,
233 NZU64,
234}
235
236#[derive(Clone)]
238pub struct MapType(pub syn::Type);
239
240impl MapType {
241 pub fn new(s: &str) -> Self {
243 let map_type = syn::parse_str::<syn::Type>(s).expect("valid ident");
244 Self(map_type)
245 }
246}
247
248impl Default for MapType {
249 fn default() -> Self {
250 Self::new("::std::collections::HashMap")
251 }
252}
253
254impl std::fmt::Debug for MapType {
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 write!(f, "MapType({})", self.0.to_token_stream())
257 }
258}
259
260impl std::fmt::Display for MapType {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 self.0.to_token_stream().fmt(f)
263 }
264}
265
266impl<'de> serde::Deserialize<'de> for MapType {
267 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
268 where
269 D: serde::Deserializer<'de>,
270 {
271 let s = <&str>::deserialize(deserializer)?;
272 Ok(Self::new(s))
273 }
274}
275
276impl From<String> for MapType {
277 fn from(s: String) -> Self {
278 Self::new(&s)
279 }
280}
281
282impl From<&str> for MapType {
283 fn from(s: &str) -> Self {
284 Self::new(s)
285 }
286}
287
288impl From<syn::Type> for MapType {
289 fn from(t: syn::Type) -> Self {
290 Self(t)
291 }
292}
293
294#[derive(Default, Debug, Clone)]
296pub struct TypeSpaceSettings {
297 type_mod: Option<String>,
298 extra_derives: Vec<String>,
299 extra_attrs: Vec<String>,
300 struct_builder: bool,
301
302 unknown_crates: UnknownPolicy,
303 crates: BTreeMap<String, CrateSpec>,
304 map_type: MapType,
305
306 patch: BTreeMap<String, TypeSpacePatch>,
307 replace: BTreeMap<String, TypeSpaceReplace>,
308 convert: Vec<TypeSpaceConversion>,
309}
310
311#[derive(Debug, Clone)]
312struct CrateSpec {
313 version: CrateVers,
314 rename: Option<String>,
315}
316
317#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Deserialize)]
320pub enum UnknownPolicy {
321 #[default]
323 Generate,
324 Allow,
330 Deny,
336}
337
338#[derive(Debug, Clone)]
341pub enum CrateVers {
342 Version(semver::Version),
344 Any,
346 Never,
348}
349
350impl CrateVers {
351 pub fn parse(s: &str) -> Option<Self> {
353 if s == "!" {
354 Some(Self::Never)
355 } else if s == "*" {
356 Some(Self::Any)
357 } else {
358 Some(Self::Version(semver::Version::parse(s).ok()?))
359 }
360 }
361}
362
363#[derive(Debug, Default, Clone)]
365pub struct TypeSpacePatch {
366 rename: Option<String>,
367 derives: Vec<String>,
368 attrs: Vec<String>,
369}
370
371#[derive(Debug, Default, Clone)]
373pub struct TypeSpaceReplace {
374 replace_type: String,
375 impls: Vec<TypeSpaceImpl>,
376}
377
378#[derive(Debug, Clone)]
381struct TypeSpaceConversion {
382 schema: schemars::schema::SchemaObject,
383 type_name: String,
384 impls: Vec<TypeSpaceImpl>,
385}
386
387#[allow(missing_docs)]
388#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
391#[non_exhaustive]
392pub enum TypeSpaceImpl {
393 FromStr,
394 FromStringIrrefutable,
395 Display,
396 Default,
397}
398
399impl std::str::FromStr for TypeSpaceImpl {
400 type Err = String;
401
402 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
403 match s {
404 "FromStr" => Ok(Self::FromStr),
405 "Display" => Ok(Self::Display),
406 "Default" => Ok(Self::Default),
407 _ => Err(format!("{} is not a valid trait specifier", s)),
408 }
409 }
410}
411
412impl TypeSpaceSettings {
413 pub fn with_type_mod<S: AsRef<str>>(&mut self, type_mod: S) -> &mut Self {
415 self.type_mod = Some(type_mod.as_ref().to_string());
416 self
417 }
418
419 pub fn with_derive(&mut self, derive: String) -> &mut Self {
421 if !self.extra_derives.contains(&derive) {
422 self.extra_derives.push(derive);
423 }
424 self
425 }
426
427 pub fn with_attr(&mut self, attr: String) -> &mut Self {
429 if !self.extra_attrs.contains(&attr) {
430 self.extra_attrs.push(attr);
431 }
432 self
433 }
434
435 pub fn with_struct_builder(&mut self, struct_builder: bool) -> &mut Self {
437 self.struct_builder = struct_builder;
438 self
439 }
440
441 pub fn with_replacement<TS: ToString, RS: ToString, I: Iterator<Item = TypeSpaceImpl>>(
445 &mut self,
446 type_name: TS,
447 replace_type: RS,
448 impls: I,
449 ) -> &mut Self {
450 self.replace.insert(
451 type_name.to_string(),
452 TypeSpaceReplace {
453 replace_type: replace_type.to_string(),
454 impls: impls.collect(),
455 },
456 );
457 self
458 }
459
460 pub fn with_patch<S: ToString>(
465 &mut self,
466 type_name: S,
467 type_patch: &TypeSpacePatch,
468 ) -> &mut Self {
469 self.patch.insert(type_name.to_string(), type_patch.clone());
470 self
471 }
472
473 pub fn with_conversion<S: ToString, I: Iterator<Item = TypeSpaceImpl>>(
499 &mut self,
500 schema: schemars::schema::SchemaObject,
501 type_name: S,
502 impls: I,
503 ) -> &mut Self {
504 self.convert.push(TypeSpaceConversion {
505 schema,
506 type_name: type_name.to_string(),
507 impls: impls.collect(),
508 });
509 self
510 }
511
512 pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
517 self.unknown_crates = policy;
518 self
519 }
520
521 pub fn with_crate<S1: ToString>(
529 &mut self,
530 crate_name: S1,
531 version: CrateVers,
532 rename: Option<&String>,
533 ) -> &mut Self {
534 self.crates.insert(
535 crate_name.to_string(),
536 CrateSpec {
537 version,
538 rename: rename.cloned(),
539 },
540 );
541 self
542 }
543
544 pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
559 self.map_type = map_type.into();
560 self
561 }
562}
563
564impl TypeSpacePatch {
565 pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
567 self.rename = Some(rename.to_string());
568 self
569 }
570
571 pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
573 self.derives.push(derive.to_string());
574 self
575 }
576
577 pub fn with_attr<S: ToString>(&mut self, attr: S) -> &mut Self {
579 self.attrs.push(attr.to_string());
580 self
581 }
582}
583
584impl TypeSpace {
585 pub fn new(settings: &TypeSpaceSettings) -> Self {
587 let mut cache = SchemaCache::default();
588
589 settings.convert.iter().for_each(
590 |TypeSpaceConversion {
591 schema,
592 type_name,
593 impls,
594 }| {
595 cache.insert(schema, type_name, impls);
596 },
597 );
598
599 Self {
600 settings: settings.clone(),
601 cache,
602 ..Default::default()
603 }
604 }
605
606 pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
616 where
617 I: IntoIterator<Item = (S, Schema)>,
618 S: AsRef<str>,
619 {
620 self.add_ref_types_impl(
621 type_defs
622 .into_iter()
623 .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
624 )
625 }
626
627 fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
628 where
629 I: IntoIterator<Item = (RefKey, Schema)>,
630 {
631 let definitions = type_defs.into_iter().collect::<Vec<_>>();
633
634 let base_id = self.next_id;
637 let def_len = definitions.len() as u64;
638 self.next_id += def_len;
639
640 for (index, (ref_name, schema)) in definitions.iter().enumerate() {
641 self.ref_to_id
642 .insert(ref_name.clone(), TypeId(base_id + index as u64));
643 self.definitions.insert(ref_name.clone(), schema.clone());
644 }
645
646 for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
651 info!(
652 "converting type: {:?} with schema {}",
653 ref_name,
654 serde_json::to_string(&schema).unwrap()
655 );
656
657 let type_id = TypeId(base_id + index as u64);
660
661 let maybe_replace = match &ref_name {
662 RefKey::Root => None,
663 RefKey::Def(def_name) => {
664 let check_name = sanitize(def_name, Case::Pascal);
665 self.settings.replace.get(&check_name)
666 }
667 };
668
669 match maybe_replace {
670 None => {
671 let type_name = if let RefKey::Def(name) = ref_name {
672 Name::Required(name.clone())
673 } else {
674 Name::Unknown
675 };
676 self.convert_ref_type(type_name, schema, type_id)?
677 }
678
679 Some(replace_type) => {
680 let type_entry = TypeEntry::new_native(
681 replace_type.replace_type.clone(),
682 &replace_type.impls.clone(),
683 );
684 self.id_to_entry.insert(type_id, type_entry);
685 }
686 }
687 }
688
689 self.break_cycles(base_id..base_id + def_len);
692
693 for index in base_id..self.next_id {
695 let type_id = TypeId(index);
696 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
697 type_entry.finalize(self)?;
698 self.id_to_entry.insert(type_id, type_entry);
699 }
700
701 Ok(())
702 }
703
704 fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
705 let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
706 let default = metadata
707 .as_ref()
708 .and_then(|m| m.default.as_ref())
709 .cloned()
710 .map(WrappedValue::new);
711 let type_entry = match &mut type_entry.details {
712 TypeEntryDetails::Enum(details) => {
714 details.default = default;
715 type_entry
716 }
717 TypeEntryDetails::Struct(details) => {
718 details.default = default;
719 type_entry
720 }
721 TypeEntryDetails::Newtype(details) => {
722 details.default = default;
723 type_entry
724 }
725
726 TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
731 self,
732 type_name,
733 metadata,
734 type_id.clone(),
735 schema.clone(),
736 ),
737
738 TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
739
740 _ => {
743 info!(
744 "type alias {:?} {}\n{:?}",
745 type_name,
746 serde_json::to_string_pretty(&schema).unwrap(),
747 metadata
748 );
749 let subtype_id = self.assign_type(type_entry);
750 TypeEntryNewtype::from_metadata(
751 self,
752 type_name,
753 metadata,
754 subtype_id,
755 schema.clone(),
756 )
757 }
758 };
759 if let Some(entry_name) = type_entry.name() {
761 self.name_to_id.insert(entry_name.clone(), type_id.clone());
762 }
763 self.id_to_entry.insert(type_id, type_entry);
764 Ok(())
765 }
766
767 pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
770 self.add_type_with_name(schema, None)
771 }
772
773 pub fn add_type_with_name(
776 &mut self,
777 schema: &Schema,
778 name_hint: Option<String>,
779 ) -> Result<TypeId> {
780 let base_id = self.next_id;
781
782 let name = match name_hint {
783 Some(s) => Name::Suggested(s),
784 None => Name::Unknown,
785 };
786 let (type_id, _) = self.id_for_schema(name, schema)?;
787
788 for index in base_id..self.next_id {
790 let type_id = TypeId(index);
791 let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
792 type_entry.finalize(self)?;
793 self.id_to_entry.insert(type_id, type_entry);
794 }
795
796 Ok(type_id)
797 }
798
799 pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
803 let RootSchema {
804 meta_schema: _,
805 schema,
806 definitions,
807 } = schema;
808
809 let mut defs = definitions
810 .into_iter()
811 .map(|(key, schema)| (RefKey::Def(key), schema))
812 .collect::<Vec<_>>();
813
814 let root_type = schema
816 .metadata
817 .as_ref()
818 .and_then(|m| m.title.as_ref())
819 .is_some();
820
821 if root_type {
822 defs.push((RefKey::Root, schema.into()));
823 }
824
825 self.add_ref_types_impl(defs)?;
826
827 if root_type {
828 Ok(self.ref_to_id.get(&RefKey::Root).cloned())
829 } else {
830 Ok(None)
831 }
832 }
833
834 pub fn get_type(&self, type_id: &TypeId) -> Result<Type<'_>> {
836 let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
837 Ok(Type {
838 type_space: self,
839 type_entry,
840 })
841 }
842
843 pub fn uses_chrono(&self) -> bool {
845 self.uses_chrono
846 }
847
848 pub fn uses_regress(&self) -> bool {
850 self.uses_regress
851 }
852
853 pub fn uses_serde_json(&self) -> bool {
855 self.uses_serde_json
856 }
857
858 pub fn uses_uuid(&self) -> bool {
860 self.uses_uuid
861 }
862
863 pub fn iter_types(&self) -> impl Iterator<Item = Type<'_>> {
866 self.id_to_entry.values().map(move |type_entry| Type {
867 type_space: self,
868 type_entry,
869 })
870 }
871
872 pub fn to_stream(&self) -> TokenStream {
874 let mut output = OutputSpace::default();
875
876 output.add_item(
879 output::OutputSpaceMod::Error,
880 "",
881 quote! {
882 pub struct ConversionError(::std::borrow::Cow<'static, str>);
884
885 impl ::std::error::Error for ConversionError {}
886 impl ::std::fmt::Display for ConversionError {
887 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
888 -> Result<(), ::std::fmt::Error>
889 {
890 ::std::fmt::Display::fmt(&self.0, f)
891 }
892 }
893
894 impl ::std::fmt::Debug for ConversionError {
895 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
896 -> Result<(), ::std::fmt::Error>
897 {
898 ::std::fmt::Debug::fmt(&self.0, f)
899 }
900 }
901 impl From<&'static str> for ConversionError {
902 fn from(value: &'static str) -> Self {
903 Self(value.into())
904 }
905 }
906 impl From<String> for ConversionError {
907 fn from(value: String) -> Self {
908 Self(value.into())
909 }
910 }
911 },
912 );
913
914 self.id_to_entry
916 .values()
917 .for_each(|type_entry| type_entry.output(self, &mut output));
918
919 self.defaults
921 .iter()
922 .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
923
924 output.into_stream()
925 }
926
927 fn assign(&mut self) -> TypeId {
929 let id = TypeId(self.next_id);
930 self.next_id += 1;
931 id
932 }
933
934 fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
939 if let TypeEntryDetails::Reference(type_id) = ty.details {
940 type_id
941 } else if let Some(name) = ty.name() {
942 if let Some(type_id) = self.name_to_id.get(name) {
952 type_id.clone()
958 } else {
959 let type_id = self.assign();
960 self.name_to_id.insert(name.clone(), type_id.clone());
961 self.id_to_entry.insert(type_id.clone(), ty);
962 type_id
963 }
964 } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
965 type_id.clone()
966 } else {
967 let type_id = self.assign();
968 self.type_to_id.insert(ty.details.clone(), type_id.clone());
969 self.id_to_entry.insert(type_id.clone(), ty);
970 type_id
971 }
972 }
973
974 fn id_for_schema<'a>(
979 &mut self,
980 type_name: Name,
981 schema: &'a Schema,
982 ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
983 let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
984 if let Some(metadata) = metadata {
985 let default = metadata.default.clone().map(WrappedValue::new);
986 match &mut type_entry.details {
987 TypeEntryDetails::Enum(details) => {
988 details.default = default;
989 }
990 TypeEntryDetails::Struct(details) => {
991 details.default = default;
992 }
993 TypeEntryDetails::Newtype(details) => {
994 details.default = default;
995 }
996 _ => (),
997 }
998 }
999 let type_id = self.assign_type(type_entry);
1000 Ok((type_id, metadata))
1001 }
1002
1003 fn id_to_option(&mut self, id: &TypeId) -> TypeId {
1005 self.assign_type(TypeEntryDetails::Option(id.clone()).into())
1006 }
1007
1008 fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
1010 TypeEntryDetails::Option(self.assign_type(ty)).into()
1011 }
1012
1013 fn id_to_box(&mut self, id: &TypeId) -> TypeId {
1015 self.assign_type(TypeEntryDetails::Box(id.clone()).into())
1016 }
1017}
1018
1019impl ToTokens for TypeSpace {
1020 fn to_tokens(&self, tokens: &mut TokenStream) {
1021 tokens.extend(self.to_stream())
1022 }
1023}
1024
1025impl Type<'_> {
1026 pub fn name(&self) -> String {
1028 let Type {
1029 type_space,
1030 type_entry,
1031 } = self;
1032 type_entry.type_name(type_space)
1033 }
1034
1035 pub fn ident(&self) -> TokenStream {
1038 let Type {
1039 type_space,
1040 type_entry,
1041 } = self;
1042 type_entry.type_ident(type_space, &type_space.settings.type_mod)
1043 }
1044
1045 pub fn parameter_ident(&self) -> TokenStream {
1049 let Type {
1050 type_space,
1051 type_entry,
1052 } = self;
1053 type_entry.type_parameter_ident(type_space, None)
1054 }
1055
1056 pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1061 let Type {
1062 type_space,
1063 type_entry,
1064 } = self;
1065 type_entry.type_parameter_ident(type_space, Some(lifetime))
1066 }
1067
1068 pub fn describe(&self) -> String {
1070 self.type_entry.describe()
1071 }
1072
1073 pub fn details(&self) -> TypeDetails<'_> {
1075 match &self.type_entry.details {
1076 TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1078 TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1079 TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1080
1081 TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1083 TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1084 TypeEntryDetails::Map(key_id, value_id) => {
1085 TypeDetails::Map(key_id.clone(), value_id.clone())
1086 }
1087 TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1088 TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1089 TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1090 TypeEntryDetails::Array(type_id, length) => {
1091 TypeDetails::Array(type_id.clone(), *length)
1092 }
1093
1094 TypeEntryDetails::Unit => TypeDetails::Unit,
1096 TypeEntryDetails::Native(TypeEntryNative {
1097 type_name: name, ..
1098 })
1099 | TypeEntryDetails::Integer(name)
1100 | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1101 TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1102 TypeEntryDetails::String => TypeDetails::String,
1103 TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1104
1105 TypeEntryDetails::Reference(_) => unreachable!(),
1107 }
1108 }
1109
1110 pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1112 let Type {
1113 type_space,
1114 type_entry,
1115 } = self;
1116 type_entry.has_impl(type_space, impl_name)
1117 }
1118
1119 pub fn builder(&self) -> Option<TokenStream> {
1121 let Type {
1122 type_space,
1123 type_entry,
1124 } = self;
1125
1126 if !type_space.settings.struct_builder {
1127 return None;
1128 }
1129
1130 match &type_entry.details {
1131 TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1132 match &type_space.settings.type_mod {
1133 Some(type_mod) => {
1134 let type_mod = format_ident!("{}", type_mod);
1135 let type_name = format_ident!("{}", name);
1136 Some(quote! { #type_mod :: builder :: #type_name })
1137 }
1138 None => {
1139 let type_name = format_ident!("{}", name);
1140 Some(quote! { builder :: #type_name })
1141 }
1142 }
1143 }
1144 _ => None,
1145 }
1146 }
1147}
1148
1149impl<'a> TypeEnum<'a> {
1150 pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1152 self.variants_info().map(|info| (info.name, info.details))
1153 }
1154
1155 pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1157 self.details.variants.iter().map(move |variant| {
1158 let details = match &variant.details {
1159 type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1160 type_entry::VariantDetails::Item(type_id) => {
1163 TypeEnumVariant::Tuple(vec![type_id.clone()])
1164 }
1165 type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1166 type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1167 properties
1168 .iter()
1169 .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1170 .collect(),
1171 ),
1172 };
1173 TypeEnumVariantInfo {
1174 name: variant.ident_name.as_ref().unwrap(),
1175 description: variant.description.as_deref(),
1176 details,
1177 }
1178 })
1179 }
1180}
1181
1182impl<'a> TypeStruct<'a> {
1183 pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1185 self.details
1186 .properties
1187 .iter()
1188 .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1189 }
1190
1191 pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo<'a>> {
1193 self.details
1194 .properties
1195 .iter()
1196 .map(move |prop| TypeStructPropInfo {
1197 name: prop.name.as_str(),
1198 description: prop.description.as_deref(),
1199 required: matches!(&prop.state, StructPropertyState::Required),
1200 type_id: prop.type_id.clone(),
1201 })
1202 }
1203}
1204
1205impl TypeNewtype<'_> {
1206 pub fn inner(&self) -> TypeId {
1208 self.details.type_id.clone()
1209 }
1210}
1211
1212#[cfg(test)]
1213mod tests {
1214 use schema::Schema;
1215 use schemars::{schema_for, JsonSchema};
1216 use serde::Serialize;
1217 use serde_json::json;
1218 use std::collections::HashSet;
1219
1220 use crate::{
1221 output::OutputSpace,
1222 test_util::validate_output,
1223 type_entry::{TypeEntryEnum, VariantDetails},
1224 Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1225 };
1226
1227 #[allow(dead_code)]
1228 #[derive(Serialize, JsonSchema)]
1229 struct Blah {
1230 blah: String,
1231 }
1232
1233 #[allow(dead_code)]
1234 #[derive(Serialize, JsonSchema)]
1235 #[serde(rename_all = "camelCase")]
1236 enum E {
1239 A,
1241 B,
1243 C(Blah),
1246 D {
1248 dd: String,
1250 },
1251 }
1259
1260 #[allow(dead_code)]
1261 #[derive(JsonSchema)]
1262 #[serde(rename_all = "camelCase")]
1263 struct Foo {
1264 #[serde(default)]
1266 bar: Option<String>,
1267 baz_baz: i32,
1268 e: E,
1270 }
1271
1272 #[test]
1273 fn test_simple() {
1274 let schema = schema_for!(Foo);
1275 println!("{:#?}", schema);
1276 let mut type_space = TypeSpace::default();
1277 type_space.add_ref_types(schema.definitions).unwrap();
1278 let (ty, _) = type_space
1279 .convert_schema_object(
1280 Name::Unknown,
1281 &schemars::schema::Schema::Object(schema.schema.clone()),
1282 &schema.schema,
1283 )
1284 .unwrap();
1285
1286 println!("{:#?}", ty);
1287
1288 let mut output = OutputSpace::default();
1289 ty.output(&type_space, &mut output);
1290 println!("{}", output.into_stream());
1291
1292 for ty in type_space.id_to_entry.values() {
1293 println!("{:#?}", ty);
1294 let mut output = OutputSpace::default();
1295 ty.output(&type_space, &mut output);
1296 println!("{}", output.into_stream());
1297 }
1298 }
1299
1300 #[test]
1301 fn test_external_references() {
1302 let schema = json!({
1303 "$schema": "http://json-schema.org/draft-04/schema#",
1304 "definitions": {
1305 "somename": {
1306 "$ref": "#/definitions/someothername",
1307 "required": [ "someproperty" ]
1308 },
1309 "someothername": {
1310 "type": "object",
1311 "properties": {
1312 "someproperty": {
1313 "type": "string"
1314 }
1315 }
1316 }
1317 }
1318 });
1319 let schema = serde_json::from_value(schema).unwrap();
1320 println!("{:#?}", schema);
1321 let settings = TypeSpaceSettings::default();
1322 let mut type_space = TypeSpace::new(&settings);
1323 type_space.add_root_schema(schema).unwrap();
1324 let tokens = type_space.to_stream().to_string();
1325 println!("{}", tokens);
1326 assert!(tokens
1327 .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1328 }
1329
1330 #[test]
1331 fn test_convert_enum_string() {
1332 #[allow(dead_code)]
1333 #[derive(JsonSchema)]
1334 #[serde(rename_all = "camelCase")]
1335 enum SimpleEnum {
1336 DotCom,
1337 Grizz,
1338 Kenneth,
1339 }
1340
1341 let schema = schema_for!(SimpleEnum);
1342 println!("{:#?}", schema);
1343
1344 let mut type_space = TypeSpace::default();
1345 type_space.add_ref_types(schema.definitions).unwrap();
1346 let (ty, _) = type_space
1347 .convert_schema_object(
1348 Name::Unknown,
1349 &schemars::schema::Schema::Object(schema.schema.clone()),
1350 &schema.schema,
1351 )
1352 .unwrap();
1353
1354 match ty.details {
1355 TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1356 for variant in &variants {
1357 assert_eq!(variant.details, VariantDetails::Simple);
1358 }
1359 let var_names = variants
1360 .iter()
1361 .map(|variant| variant.ident_name.as_ref().unwrap().clone())
1362 .collect::<HashSet<_>>();
1363 assert_eq!(
1364 var_names,
1365 ["DotCom", "Grizz", "Kenneth",]
1366 .iter()
1367 .map(ToString::to_string)
1368 .collect::<HashSet<_>>()
1369 );
1370 }
1371 _ => {
1372 let mut output = OutputSpace::default();
1373 ty.output(&type_space, &mut output);
1374 println!("{}", output.into_stream());
1375 panic!();
1376 }
1377 }
1378 }
1379
1380 #[test]
1381 fn test_string_enum_with_null() {
1382 let original_schema = json!({ "$ref": "xxx"});
1383 let enum_values = vec![
1384 json!("Shadrach"),
1385 json!("Meshach"),
1386 json!("Abednego"),
1387 json!(null),
1388 ];
1389
1390 let mut type_space = TypeSpace::default();
1391 let (te, _) = type_space
1392 .convert_enum_string(
1393 Name::Required("OnTheGo".to_string()),
1394 &serde_json::from_value(original_schema).unwrap(),
1395 &None,
1396 &enum_values,
1397 None,
1398 )
1399 .unwrap();
1400
1401 if let TypeEntryDetails::Option(id) = &te.details {
1402 let ote = type_space.id_to_entry.get(id).unwrap();
1403 if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1404 let variants = variants
1405 .iter()
1406 .map(|v| match v.details {
1407 VariantDetails::Simple => v.ident_name.as_ref().unwrap().clone(),
1408 _ => panic!("unexpected variant type"),
1409 })
1410 .collect::<HashSet<_>>();
1411
1412 assert_eq!(
1413 variants,
1414 enum_values
1415 .iter()
1416 .flat_map(|j| j.as_str().map(ToString::to_string))
1417 .collect::<HashSet<_>>()
1418 );
1419 } else {
1420 panic!("not the sub-type we expected {:#?}", te)
1421 }
1422 } else {
1423 panic!("not the type we expected {:#?}", te)
1424 }
1425 }
1426
1427 #[test]
1428 fn test_alias() {
1429 #[allow(dead_code)]
1430 #[derive(JsonSchema, Schema)]
1431 struct Stuff(Vec<String>);
1432
1433 #[allow(dead_code)]
1434 #[derive(JsonSchema, Schema)]
1435 struct Things {
1436 a: String,
1437 b: Stuff,
1438 }
1439
1440 validate_output::<Things>();
1441 }
1442
1443 #[test]
1444 fn test_builder_name() {
1445 #[allow(dead_code)]
1446 #[derive(JsonSchema)]
1447 struct TestStruct {
1448 x: u32,
1449 }
1450
1451 let mut type_space = TypeSpace::default();
1452 let schema = schema_for!(TestStruct);
1453 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1454 let ty = type_space.get_type(&type_id).unwrap();
1455
1456 assert!(ty.builder().is_none());
1457
1458 let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1459 let schema = schema_for!(TestStruct);
1460 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1461 let ty = type_space.get_type(&type_id).unwrap();
1462
1463 assert_eq!(
1464 ty.builder().map(|ts| ts.to_string()),
1465 Some("builder :: TestStruct".to_string())
1466 );
1467
1468 let mut type_space = TypeSpace::new(
1469 TypeSpaceSettings::default()
1470 .with_type_mod("types")
1471 .with_struct_builder(true),
1472 );
1473 let schema = schema_for!(TestStruct);
1474 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1475 let ty = type_space.get_type(&type_id).unwrap();
1476
1477 assert_eq!(
1478 ty.builder().map(|ts| ts.to_string()),
1479 Some("types :: builder :: TestStruct".to_string())
1480 );
1481
1482 #[allow(dead_code)]
1483 #[derive(JsonSchema)]
1484 enum TestEnum {
1485 X,
1486 Y,
1487 }
1488 let mut type_space = TypeSpace::new(
1489 TypeSpaceSettings::default()
1490 .with_type_mod("types")
1491 .with_struct_builder(true),
1492 );
1493 let schema = schema_for!(TestEnum);
1494 let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1495 let ty = type_space.get_type(&type_id).unwrap();
1496 assert!(ty.builder().is_none());
1497 }
1498}