ploidy_codegen_rust/
schema.rs1use ploidy_core::{codegen::IntoCode, ir::SchemaIrTypeView};
2use proc_macro2::TokenStream;
3use quote::{ToTokens, TokenStreamExt, quote};
4
5use super::{
6 enum_::CodegenEnum, inlines::CodegenInlines, naming::CodegenTypeName, struct_::CodegenStruct,
7 tagged::CodegenTagged, untagged::CodegenUntagged,
8};
9
10#[derive(Debug)]
12pub struct CodegenSchemaType<'a> {
13 ty: &'a SchemaIrTypeView<'a>,
14}
15
16impl<'a> CodegenSchemaType<'a> {
17 pub fn new(ty: &'a SchemaIrTypeView<'a>) -> Self {
18 Self { ty }
19 }
20}
21
22impl ToTokens for CodegenSchemaType<'_> {
23 fn to_tokens(&self, tokens: &mut TokenStream) {
24 let name = CodegenTypeName::Schema(self.ty);
25 let ty = match self.ty {
26 SchemaIrTypeView::Struct(_, view) => CodegenStruct::new(name, view).into_token_stream(),
27 SchemaIrTypeView::Enum(_, view) => CodegenEnum::new(name, view).into_token_stream(),
28 SchemaIrTypeView::Tagged(_, view) => CodegenTagged::new(name, view).into_token_stream(),
29 SchemaIrTypeView::Untagged(_, view) => {
30 CodegenUntagged::new(name, view).into_token_stream()
31 }
32 };
33 let inlines = CodegenInlines::Schema(self.ty);
34 tokens.append_all(quote! {
35 #ty
36 #inlines
37 });
38 }
39}
40
41impl IntoCode for CodegenSchemaType<'_> {
42 type Code = (String, TokenStream);
43
44 fn into_code(self) -> Self::Code {
45 let name = CodegenTypeName::Schema(self.ty);
46 (
47 format!("src/types/{}.rs", name.into_module_name().display()),
48 self.into_token_stream(),
49 )
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 use ploidy_core::{
58 ir::{IrGraph, IrSpec, SchemaIrTypeView},
59 parse::Document,
60 };
61 use pretty_assertions::assert_eq;
62 use syn::parse_quote;
63
64 use crate::CodegenGraph;
65
66 #[test]
67 fn test_schema_inline_types_order() {
68 let doc = Document::from_yaml(indoc::indoc! {"
71 openapi: 3.0.0
72 info:
73 title: Test API
74 version: 1.0.0
75 paths: {}
76 components:
77 schemas:
78 Container:
79 type: object
80 properties:
81 zebra:
82 type: object
83 properties:
84 name:
85 type: string
86 mango:
87 type: object
88 properties:
89 name:
90 type: string
91 apple:
92 type: object
93 properties:
94 name:
95 type: string
96 "})
97 .unwrap();
98
99 let spec = IrSpec::from_doc(&doc).unwrap();
100 let ir = IrGraph::new(&spec);
101 let graph = CodegenGraph::new(ir);
102
103 let schema = graph.schemas().find(|s| s.name() == "Container");
104 let Some(schema @ SchemaIrTypeView::Struct(_, _)) = &schema else {
105 panic!("expected struct `Container`; got `{schema:?}`");
106 };
107
108 let codegen = CodegenSchemaType::new(schema);
109
110 let actual: syn::File = parse_quote!(#codegen);
111 let expected: syn::File = parse_quote! {
115 #[derive(Debug, Clone, PartialEq, Eq, Hash, Default, ::ploidy_util::serde::Serialize, ::ploidy_util::serde::Deserialize)]
116 #[serde(crate = "::ploidy_util::serde")]
117 pub struct Container {
118 #[serde(default, skip_serializing_if = "::ploidy_util::absent::AbsentOr::is_absent",)]
119 pub zebra: ::ploidy_util::absent::AbsentOr<crate::types::container::types::Zebra>,
120 #[serde(default, skip_serializing_if = "::ploidy_util::absent::AbsentOr::is_absent",)]
121 pub mango: ::ploidy_util::absent::AbsentOr<crate::types::container::types::Mango>,
122 #[serde(default, skip_serializing_if = "::ploidy_util::absent::AbsentOr::is_absent",)]
123 pub apple: ::ploidy_util::absent::AbsentOr<crate::types::container::types::Apple>,
124 }
125 pub mod types {
126 #[derive(Debug, Clone, PartialEq, Eq, Hash, Default, ::ploidy_util::serde::Serialize, ::ploidy_util::serde::Deserialize)]
127 #[serde(crate = "::ploidy_util::serde")]
128 pub struct Apple {
129 #[serde(default, skip_serializing_if = "::ploidy_util::absent::AbsentOr::is_absent",)]
130 pub name: ::ploidy_util::absent::AbsentOr<::std::string::String>,
131 }
132 #[derive(Debug, Clone, PartialEq, Eq, Hash, Default, ::ploidy_util::serde::Serialize, ::ploidy_util::serde::Deserialize)]
133 #[serde(crate = "::ploidy_util::serde")]
134 pub struct Mango {
135 #[serde(default, skip_serializing_if = "::ploidy_util::absent::AbsentOr::is_absent",)]
136 pub name: ::ploidy_util::absent::AbsentOr<::std::string::String>,
137 }
138 #[derive(Debug, Clone, PartialEq, Eq, Hash, Default, ::ploidy_util::serde::Serialize, ::ploidy_util::serde::Deserialize)]
139 #[serde(crate = "::ploidy_util::serde")]
140 pub struct Zebra {
141 #[serde(default, skip_serializing_if = "::ploidy_util::absent::AbsentOr::is_absent",)]
142 pub name: ::ploidy_util::absent::AbsentOr<::std::string::String>,
143 }
144 }
145 };
146 assert_eq!(actual, expected);
147 }
148}