1use camo_core as camo;
2use std::{convert::TryFrom, fmt};
3
4#[derive(Clone, Debug, PartialEq)]
6pub enum Definition {
7 Interface(Interface),
9 Alias(TypeAlias),
11}
12
13impl Definition {
14 pub fn name(&self) -> &str {
16 match self {
17 Definition::Interface(i) => &i.name,
18 Definition::Alias(a) => &a.name,
19 }
20 }
21
22 pub fn export(&self) -> bool {
24 match self {
25 Definition::Interface(i) => i.export,
26 Definition::Alias(a) => a.export,
27 }
28 }
29}
30
31impl From<Interface> for Definition {
32 fn from(value: Interface) -> Self {
33 Definition::Interface(value)
34 }
35}
36
37impl From<TypeAlias> for Definition {
38 fn from(value: TypeAlias) -> Self {
39 Definition::Alias(value)
40 }
41}
42
43#[derive(Clone, Copy)]
44struct Renamer(Option<camo::RenameRule>);
45
46impl Renamer {
47 fn rename_field(&self, name: &str) -> String {
48 let Self(rule) = self;
49 match rule {
50 Some(camo::RenameRule::LowerCase) => name.to_lowercase(),
51 Some(camo::RenameRule::UpperCase) => name.to_uppercase(),
52 Some(camo::RenameRule::PascalCase) => snake_to_non_snake_case(true, name),
53 Some(camo::RenameRule::CamelCase) => snake_to_non_snake_case(false, name),
54 Some(camo::RenameRule::SnakeCase) => name.to_string(),
55 Some(camo::RenameRule::ScreamingSnakeCase) => name.to_uppercase(),
56 Some(camo::RenameRule::KebabCase) => name.replace('_', "-"),
57 Some(camo::RenameRule::ScreamingKebabCase) => name.to_uppercase().replace('_', "-"),
58 None => name.to_string(),
59 }
60 }
61
62 fn rename_type(&self, name: &str) -> String {
63 let Self(rule) = self;
64 match rule {
65 Some(camo::RenameRule::LowerCase) => name.to_lowercase(),
66 Some(camo::RenameRule::UpperCase) => name.to_uppercase(),
67 Some(camo::RenameRule::PascalCase) => name.to_string(),
68 Some(camo::RenameRule::CamelCase) => name[..1].to_ascii_lowercase() + &name[1..],
69 Some(camo::RenameRule::SnakeCase) => pascal_to_separated_case('_', name),
70 Some(camo::RenameRule::ScreamingSnakeCase) => {
71 pascal_to_separated_case('_', name).to_uppercase()
72 }
73 Some(camo::RenameRule::KebabCase) => pascal_to_separated_case('-', name),
74 Some(camo::RenameRule::ScreamingKebabCase) => {
75 pascal_to_separated_case('-', name).to_uppercase()
76 }
77 None => name.to_string(),
78 }
79 }
80}
81
82fn snake_to_non_snake_case(capitalize_first: bool, field: &str) -> String {
83 let mut result = String::new();
84 let mut capitalize = capitalize_first;
85 for ch in field.chars() {
86 if ch == '_' {
87 capitalize = true;
88 } else if capitalize {
89 result.push(ch.to_ascii_uppercase());
90 capitalize = false;
91 } else {
92 result.push(ch);
93 }
94 }
95 result
96}
97
98fn pascal_to_separated_case(separator: char, name: &str) -> String {
99 let mut result = String::new();
100 for (i, ch) in name.char_indices() {
101 if i > 0 && ch.is_uppercase() {
102 result.push(separator);
103 }
104 result.push(ch.to_ascii_lowercase());
105 }
106 result
107}
108
109impl From<camo::Container> for Definition {
110 fn from(container: camo::Container) -> Self {
111 let rename = Renamer(container.attributes.rename);
112 let rename_all = Renamer(container.attributes.rename_all);
113 let tag_rule = container.attributes.tag;
114 let content_rule = container.attributes.content;
115
116 match container.item {
117 camo::Item::Struct(s) => match s.content {
118 camo::StructContent::NamedFields(fields) => Definition::Interface(Interface {
119 export: s.visibility.is_pub(),
120 name: rename.rename_type(s.name),
121 parameters: s
122 .parameters
123 .into_iter()
124 .filter_map(|parameter| match parameter {
125 camo::GenericParameter::Lifetime(_) => None,
127 camo::GenericParameter::Type(ty) => Some(ty),
128 })
129 .collect(),
130 fields: fields
131 .into_iter()
132 .map(|field| Field {
133 name: rename_all.rename_field(field.name),
134 ty: Type::from(field.ty),
135 optional: false,
136 })
137 .collect(),
138 }),
139 camo::StructContent::UnnamedField(field) => {
140 Definition::Alias(TypeAlias {
141 export: s.visibility.is_pub(),
142 name: rename.rename_type(s.name),
143 parameters: s
144 .parameters
145 .into_iter()
146 .filter_map(|parameter| match parameter {
147 camo::GenericParameter::Lifetime(_) => None,
149 camo::GenericParameter::Type(ty) => Some(ty),
150 })
151 .collect(),
152 ty: Type::from(field.ty),
153 })
154 }
155 },
156 camo::Item::Enum(ty) => Definition::Alias(if let Some(tag) = tag_rule {
157 if let Some(content) = content_rule {
158 TypeAlias::adjacently_tagged(rename, rename_all, tag, content, ty)
159 } else {
160 TypeAlias::internally_tagged(rename, rename_all, tag, ty)
161 }
162 } else {
163 TypeAlias::externally_tagged(rename, rename_all, ty)
164 }),
165 }
166 }
167}
168
169impl fmt::Display for Definition {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 match self {
172 Definition::Interface(ty) => write!(f, "{}", ty),
173 Definition::Alias(ty) => write!(f, "{}", ty),
174 }
175 }
176}
177
178#[derive(Clone, Debug, PartialEq)]
190pub struct Interface {
191 pub export: bool,
193 pub name: String,
195 pub parameters: Vec<&'static str>,
197 pub fields: Vec<Field>,
199}
200
201impl fmt::Display for Interface {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 if self.export {
204 write!(f, "export ")?;
205 }
206 write!(f, "interface {}", self.name)?;
207 if !self.parameters.is_empty() {
208 write!(f, "<")?;
209 for parameter in &self.parameters {
210 write!(f, "{}", parameter)?;
211 }
212 write!(f, ">")?;
213 }
214 writeln!(f, " {{")?;
215 for field in &self.fields {
216 writeln!(f, "\t{}", field)?;
217 }
218 writeln!(f, "}}")
219 }
220}
221
222#[derive(Clone, Debug, PartialEq)]
224pub struct Field {
225 pub name: String,
227 pub ty: Type,
229 pub optional: bool,
231}
232
233impl fmt::Display for Field {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 let name = &self.name;
236 let ty = &self.ty;
237
238 if is_valid_identifier(self.name.as_str()) {
239 if self.optional {
240 write!(f, "{name}?: {ty};")
241 } else {
242 write!(f, "{name}: {ty};")
243 }
244 } else if self.optional {
245 write!(f, r#"'{name}'?: {ty};"#)
246 } else {
247 write!(f, r#"'{name}': {ty};"#)
248 }
249 }
250}
251
252fn is_valid_identifier(string: &str) -> bool {
253 let mut chars = string.chars();
254 if let Some(c) = chars.next() {
255 if !c.is_alphabetic() && c != '_' {
256 return false;
257 }
258 }
259 chars.all(|c| c.is_alphanumeric() || c == '_')
260}
261
262#[derive(Clone, Debug, PartialEq)]
271pub struct TypeAlias {
272 pub export: bool,
274 pub name: String,
276 pub parameters: Vec<&'static str>,
278 pub ty: Type,
280}
281
282impl TypeAlias {
283 pub fn alias<T: Into<Type>>(name: &str, ty: T) -> Self {
285 Self {
286 export: false,
287 name: String::from(name),
288 parameters: Vec::new(),
289 ty: ty.into(),
290 }
291 }
292
293 pub fn exported(self) -> Self {
295 Self {
296 export: true,
297 ..self
298 }
299 }
300
301 fn externally_tagged(rename: Renamer, rename_all: Renamer, ty: camo::Enum) -> Self {
302 Self {
303 export: ty.visibility.is_pub(),
304 name: rename.rename_type(ty.name),
305 parameters: ty
306 .parameters
307 .into_iter()
308 .filter_map(|parameter| match parameter {
309 camo::GenericParameter::Lifetime(_) => None,
311 camo::GenericParameter::Type(ty) => Some(ty),
312 })
313 .collect(),
314 ty: Type::Union(UnionType::externally_tagged(rename_all, ty.variants)),
315 }
316 }
317
318 fn adjacently_tagged(
319 rename: Renamer,
320 rename_all: Renamer,
321 tag: &'static str,
322 content: &'static str,
323 ty: camo::Enum,
324 ) -> Self {
325 Self {
326 export: ty.visibility.is_pub(),
327 name: rename.rename_type(ty.name),
328 parameters: ty
329 .parameters
330 .into_iter()
331 .filter_map(|parameter| match parameter {
332 camo::GenericParameter::Lifetime(_) => None,
334 camo::GenericParameter::Type(ty) => Some(ty),
335 })
336 .collect(),
337 ty: Type::Union(UnionType::adjacently_tagged(
338 rename_all,
339 tag,
340 content,
341 ty.variants,
342 )),
343 }
344 }
345
346 fn internally_tagged(
347 rename: Renamer,
348 rename_all: Renamer,
349 tag: &'static str,
350 ty: camo::Enum,
351 ) -> Self {
352 Self {
353 export: ty.visibility.is_pub(),
354 name: rename.rename_field(ty.name),
355 parameters: ty
356 .parameters
357 .into_iter()
358 .filter_map(|parameter| match parameter {
359 camo::GenericParameter::Lifetime(_) => None,
361 camo::GenericParameter::Type(ty) => Some(ty),
362 })
363 .collect(),
364 ty: Type::Union(UnionType::internally_tagged(rename_all, tag, ty.variants)),
365 }
366 }
367}
368
369impl fmt::Display for TypeAlias {
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 if self.export {
372 write!(f, "export ")?;
373 }
374 write!(f, "type {}", self.name)?;
375 if !self.parameters.is_empty() {
376 write!(f, "<")?;
377 for parameter in &self.parameters {
378 write!(f, "{}", parameter)?;
379 }
380 write!(f, ">")?;
381 }
382 if self.ty.is_union() {
383 writeln!(f, " ={};", self.ty)
384 } else {
385 writeln!(f, " = {};", self.ty)
386 }
387 }
388}
389
390#[derive(Clone, Debug, PartialEq)]
402pub struct UnionType {
403 pub variants: Vec<Variant>,
405}
406
407impl UnionType {
408 fn externally_tagged(rename_all: Renamer, variants: Vec<camo::Variant>) -> Self {
409 Self {
410 variants: variants
411 .into_iter()
412 .map(|variant| Variant::externally_tagged(rename_all, variant))
413 .collect(),
414 }
415 }
416
417 fn adjacently_tagged(
418 rename_all: Renamer,
419 tag: &'static str,
420 content: &'static str,
421 variants: Vec<camo::Variant>,
422 ) -> Self {
423 Self {
424 variants: variants
425 .into_iter()
426 .map(|variant| Variant::adjacently_tagged(rename_all, tag, content, variant))
427 .collect(),
428 }
429 }
430
431 fn internally_tagged(
432 rename_all: Renamer,
433 tag: &'static str,
434 variants: Vec<camo::Variant>,
435 ) -> Self {
436 Self {
437 variants: variants
438 .into_iter()
439 .map(|variant| Variant::internally_tagged(rename_all, tag, variant))
440 .collect(),
441 }
442 }
443}
444
445impl fmt::Display for UnionType {
446 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447 for variant in &self.variants {
448 write!(f, "\n\t| {}", variant)?;
449 }
450 Ok(())
451 }
452}
453
454#[derive(Clone, Debug, PartialEq)]
456pub struct Variant(pub Type);
457
458impl Variant {
459 fn externally_tagged(rename_all: Renamer, variant: camo::Variant) -> Self {
460 let variant_renamer = match variant.attributes.rename {
461 Some(rename) => Renamer(Some(rename)),
462 None => rename_all,
463 };
464 let field_renamer = Renamer(variant.attributes.rename_all);
465 match variant.content {
466 camo::VariantContent::Unit => Self(Type::Literal(LiteralType::String(
467 variant_renamer.rename_type(variant.name),
468 ))),
469 camo::VariantContent::Unnamed(ty) => Self(Type::Object(ObjectType {
470 fields: Vec::from([Field {
471 name: variant_renamer.rename_type(variant.name),
472 ty: Type::from(ty),
473 optional: false,
474 }]),
475 })),
476 camo::VariantContent::Named(fields) => Self(Type::Object(ObjectType {
477 fields: Vec::from([Field {
478 name: variant_renamer.rename_type(variant.name),
479 ty: Type::Object(ObjectType {
480 fields: fields
481 .into_iter()
482 .map(|field| Field {
483 name: field_renamer.rename_field(field.name),
484 ty: Type::from(field.ty),
485 optional: false,
486 })
487 .collect(),
488 }),
489 optional: false,
490 }]),
491 })),
492 }
493 }
494
495 fn adjacently_tagged(
496 rename_all: Renamer,
497 tag: &'static str,
498 content: &'static str,
499 variant: camo::Variant,
500 ) -> Self {
501 let variant_renamer = match variant.attributes.rename {
502 Some(rename) => Renamer(Some(rename)),
503 None => rename_all,
504 };
505 let field_renamer = Renamer(variant.attributes.rename_all);
506 match variant.content {
507 camo::VariantContent::Unit => Self(Type::Object(ObjectType {
508 fields: Vec::from([Field {
509 name: String::from(tag),
510 ty: Type::Literal(LiteralType::String(
511 variant_renamer.rename_type(variant.name),
512 )),
513 optional: false,
514 }]),
515 })),
516 camo::VariantContent::Unnamed(ty) => Self(Type::Object(ObjectType {
517 fields: Vec::from([
518 Field {
519 name: String::from(tag),
520 ty: Type::Literal(LiteralType::String(
521 variant_renamer.rename_type(variant.name),
522 )),
523 optional: false,
524 },
525 Field {
526 name: String::from(content),
527 ty: Type::from(ty),
528 optional: false,
529 },
530 ]),
531 })),
532 camo::VariantContent::Named(fields) => Self(Type::Object(ObjectType {
533 fields: Vec::from([
534 Field {
535 name: String::from(tag),
536 ty: Type::Literal(LiteralType::String(
537 variant_renamer.rename_type(variant.name),
538 )),
539 optional: false,
540 },
541 Field {
542 name: String::from(content),
543 ty: Type::Object(ObjectType {
544 fields: fields
545 .into_iter()
546 .map(|field| Field {
547 name: field_renamer.rename_field(field.name),
548 ty: Type::from(field.ty),
549 optional: false,
550 })
551 .collect(),
552 }),
553 optional: false,
554 },
555 ]),
556 })),
557 }
558 }
559
560 fn internally_tagged(rename_all: Renamer, tag: &'static str, variant: camo::Variant) -> Self {
561 let variant_renamer = match variant.attributes.rename {
562 Some(rename) => Renamer(Some(rename)),
563 None => rename_all,
564 };
565 let field_renamer = Renamer(variant.attributes.rename_all);
566 match variant.content {
567 camo::VariantContent::Unit => Self(Type::Object(ObjectType {
568 fields: Vec::from([Field {
569 name: String::from(tag),
570 ty: Type::Literal(LiteralType::String(
571 variant_renamer.rename_type(variant.name),
572 )),
573 optional: false,
574 }]),
575 })),
576 camo::VariantContent::Unnamed(ty) => Self(Type::Intersection(IntersectionType {
577 left: Box::new(Type::Object(ObjectType {
578 fields: Vec::from([Field {
579 name: String::from(tag),
580 ty: Type::Literal(LiteralType::String(
581 variant_renamer.rename_type(variant.name),
582 )),
583 optional: false,
584 }]),
585 })),
586 right: Box::new(Type::from(ty)),
587 })),
588 camo::VariantContent::Named(fields) => Self(Type::Intersection(IntersectionType {
589 left: Box::new(Type::Object(ObjectType {
590 fields: Vec::from([Field {
591 name: String::from(tag),
592 ty: Type::Literal(LiteralType::String(
593 variant_renamer.rename_type(variant.name),
594 )),
595 optional: false,
596 }]),
597 })),
598 right: Box::new(Type::Object(ObjectType {
599 fields: fields
600 .into_iter()
601 .map(|field| Field {
602 name: field_renamer.rename_field(field.name),
603 ty: Type::from(field.ty),
604 optional: false,
605 })
606 .collect(),
607 })),
608 })),
609 }
610 }
611}
612
613impl fmt::Display for Variant {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615 write!(f, "{}", self.0)
616 }
617}
618
619#[derive(Clone, Debug, PartialEq)]
622pub enum Type {
623 Builtin(BuiltinType),
625 Path(TypePath),
628 Object(ObjectType),
630 Literal(LiteralType),
632 Array(ArrayType),
634 Union(UnionType),
636 Intersection(IntersectionType),
638}
639
640impl Type {
641 fn is_union(&self) -> bool {
642 matches!(self, Self::Union(..))
643 }
644}
645
646impl From<&str> for Type {
647 fn from(value: &str) -> Self {
648 Self::Path(TypePath::from(value))
649 }
650}
651
652impl From<String> for Type {
653 fn from(value: String) -> Self {
654 Self::Path(TypePath::from(value))
655 }
656}
657
658impl From<BuiltinType> for Type {
659 fn from(value: BuiltinType) -> Self {
660 Self::Builtin(value)
661 }
662}
663
664impl From<TypePath> for Type {
665 fn from(value: TypePath) -> Self {
666 Self::Path(value)
667 }
668}
669
670impl From<ObjectType> for Type {
671 fn from(value: ObjectType) -> Self {
672 Self::Object(value)
673 }
674}
675
676impl From<LiteralType> for Type {
677 fn from(value: LiteralType) -> Self {
678 Self::Literal(value)
679 }
680}
681
682impl From<ArrayType> for Type {
683 fn from(value: ArrayType) -> Self {
684 Self::Array(value)
685 }
686}
687
688impl From<IntersectionType> for Type {
689 fn from(value: IntersectionType) -> Self {
690 Self::Intersection(value)
691 }
692}
693
694impl From<camo::Type> for Type {
695 fn from(ty: camo::Type) -> Self {
696 match ty {
697 camo::Type::Path(ty) => match camo::BuiltinType::try_from(ty) {
698 Ok(ty) => Type::Builtin(BuiltinType::from(ty)),
699 Err(ty) => {
700 if let Some(segment) = ty.segments.first() {
701 match segment.name {
702 "String" => {
703 return Type::Builtin(BuiltinType::String);
704 }
705 "Vec" => {
706 let component_ty = match segment.arguments.first().unwrap().clone()
707 {
708 camo::GenericArgument::Type(ty) => ty,
709 camo::GenericArgument::Lifetime(_) => {
710 panic!("unexpected lifetime argument provided to Vec")
711 }
712 };
713 return Type::Array(ArrayType::from(Type::from(component_ty)));
714 }
715 "Option" => {
716 let component_ty = match segment.arguments.first().unwrap().clone()
717 {
718 camo::GenericArgument::Type(ty) => ty,
719 camo::GenericArgument::Lifetime(_) => {
720 panic!("unexpected lifetime argument provided to Option")
721 }
722 };
723 return Type::Union(UnionType {
724 variants: Vec::from([
725 Variant(Type::from(component_ty)),
726 Variant(Type::Builtin(BuiltinType::Null)),
727 ]),
728 });
729 }
730 _ => return Type::Path(TypePath::from(ty)),
731 }
732 }
733 Type::Path(TypePath::from(ty))
734 }
735 },
736 camo::Type::Reference(ty) => {
737 if let camo::Type::Path(path) = &*ty.ty {
738 if let Some(segment) = path.segments.first() {
739 if segment.name == "str" {
740 return Type::Builtin(BuiltinType::String);
741 }
742 }
743 }
744 Type::from(*ty.ty)
745 }
746 camo::Type::Slice(ty) => Type::Array(ArrayType::from(ty)),
747 camo::Type::Array(ty) => Type::Array(ArrayType::from(ty)),
748 }
749 }
750}
751
752impl fmt::Display for Type {
753 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
754 match self {
755 Type::Builtin(ty) => write!(f, "{}", ty),
756 Type::Path(ty) => write!(f, "{}", ty),
757 Type::Object(ty) => write!(f, "{}", ty),
758 Type::Literal(ty) => write!(f, "{}", ty),
759 Type::Array(ty) => write!(f, "{}", ty),
760 Type::Union(ty) => write!(f, "{}", ty),
761 Type::Intersection(ty) => write!(f, "{}", ty),
762 }
763 }
764}
765
766#[derive(Clone, Debug, PartialEq)]
768pub enum BuiltinType {
769 Number,
771 Boolean,
773 String,
775 Object,
777 Null,
779 Undefined,
781 Never,
783 Any,
785 Unknown,
787 BigInt,
789 Symbol,
791}
792
793impl BuiltinType {
794 pub fn as_str(&self) -> &'static str {
796 match self {
797 BuiltinType::Number => "number",
798 BuiltinType::Boolean => "boolean",
799 BuiltinType::String => "string",
800 BuiltinType::Object => "object",
801 BuiltinType::Null => "null",
802 BuiltinType::Undefined => "undefined",
803 BuiltinType::Never => "never",
804 BuiltinType::Any => "any",
805 BuiltinType::Unknown => "unknown",
806 BuiltinType::BigInt => "bigint",
807 BuiltinType::Symbol => "symbol",
808 }
809 }
810}
811
812impl From<camo::BuiltinType> for BuiltinType {
813 fn from(builtin: camo::BuiltinType) -> Self {
814 match builtin {
815 camo::BuiltinType::Bool => BuiltinType::Boolean,
816 camo::BuiltinType::U8
817 | camo::BuiltinType::U16
818 | camo::BuiltinType::U32
819 | camo::BuiltinType::U64
820 | camo::BuiltinType::U128
821 | camo::BuiltinType::Usize
822 | camo::BuiltinType::I8
823 | camo::BuiltinType::I16
824 | camo::BuiltinType::I32
825 | camo::BuiltinType::I64
826 | camo::BuiltinType::I128
827 | camo::BuiltinType::Isize
828 | camo::BuiltinType::F32
829 | camo::BuiltinType::F64 => BuiltinType::Number,
830 camo::BuiltinType::Char => BuiltinType::String,
831 }
832 }
833}
834
835impl fmt::Display for BuiltinType {
836 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
837 write!(f, "{}", self.as_str())
838 }
839}
840
841#[derive(Clone, Debug, PartialEq)]
850pub struct TypePath {
851 pub segments: Vec<PathSegment>,
853}
854
855impl From<camo::TypePath> for TypePath {
856 fn from(value: camo::TypePath) -> Self {
857 Self {
858 segments: value.segments.into_iter().map(Into::into).collect(),
859 }
860 }
861}
862
863impl<const N: usize> From<[&str; N]> for TypePath {
864 fn from(value: [&str; N]) -> Self {
865 Self {
866 segments: value
867 .map(|name| PathSegment {
868 name: name.to_string(),
869 arguments: Vec::new(),
870 })
871 .to_vec(),
872 }
873 }
874}
875
876impl From<&str> for TypePath {
877 fn from(value: &str) -> Self {
878 Self {
879 segments: Vec::from([PathSegment {
880 name: value.to_string(),
881 arguments: Vec::new(),
882 }]),
883 }
884 }
885}
886
887impl From<String> for TypePath {
888 fn from(value: String) -> Self {
889 Self {
890 segments: Vec::from([PathSegment {
891 name: value,
892 arguments: Vec::new(),
893 }]),
894 }
895 }
896}
897
898impl fmt::Display for TypePath {
899 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
900 let mut iter = self.segments.iter();
901 if let Some(segment) = iter.next() {
902 write!(f, "{}", segment)?;
903 }
904 for segment in iter {
905 write!(f, ".{}", segment)?;
906 }
907 Ok(())
908 }
909}
910
911#[derive(Clone, Debug, PartialEq)]
913pub struct PathSegment {
914 pub name: String,
916 pub arguments: Vec<Type>,
918}
919
920impl From<camo::PathSegment> for PathSegment {
921 fn from(value: camo::PathSegment) -> Self {
922 Self {
923 name: value.name.to_string(),
924 arguments: value
925 .arguments
926 .into_iter()
927 .filter_map(|argument| match argument {
928 camo::GenericArgument::Type(ty) => Some(Type::from(ty)),
929 camo::GenericArgument::Lifetime(_) => None,
930 })
931 .collect(),
932 }
933 }
934}
935
936impl fmt::Display for PathSegment {
937 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
938 write!(f, "{}", self.name)?;
939 if !self.arguments.is_empty() {
940 write!(f, "<")?;
941 let mut iter = self.arguments.iter();
942 if let Some(argument) = iter.next() {
943 write!(f, "{}", argument)?;
944 }
945 for argument in iter {
946 write!(f, ", {}", argument)?;
947 }
948 write!(f, ">")?;
949 }
950 Ok(())
951 }
952}
953
954#[derive(Clone, Debug, PartialEq)]
958pub struct ObjectType {
959 pub fields: Vec<Field>,
961}
962
963impl fmt::Display for ObjectType {
964 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965 write!(f, "{{")?;
966 for field in &self.fields {
967 write!(f, " {}", field)?;
968 }
969 write!(f, " }}")
970 }
971}
972
973#[derive(Clone, Debug, PartialEq)]
982pub enum LiteralType {
983 String(String),
985}
986
987impl fmt::Display for LiteralType {
988 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
989 match self {
990 LiteralType::String(s) => write!(f, "\"{}\"", s),
991 }
992 }
993}
994
995#[derive(Clone, Debug, PartialEq)]
1002pub struct ArrayType(pub Box<Type>);
1003
1004impl From<Type> for ArrayType {
1005 fn from(value: Type) -> Self {
1006 Self(Box::new(value))
1007 }
1008}
1009
1010impl From<camo::SliceType> for ArrayType {
1011 fn from(value: camo::SliceType) -> Self {
1012 Self(Box::new(Type::from(*value.0)))
1013 }
1014}
1015
1016impl From<camo::ArrayType> for ArrayType {
1017 fn from(value: camo::ArrayType) -> Self {
1018 Self(Box::new(Type::from(*value.0)))
1019 }
1020}
1021
1022impl fmt::Display for ArrayType {
1023 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1024 write!(f, "{}[]", self.0)
1025 }
1026}
1027
1028#[derive(Clone, Debug, PartialEq)]
1038pub struct IntersectionType {
1039 pub left: Box<Type>,
1041 pub right: Box<Type>,
1043}
1044
1045impl fmt::Display for IntersectionType {
1046 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1047 write!(f, "{} & {}", self.left, self.right)
1048 }
1049}