1use proc_macro2::TokenStream;
15use quote::quote;
16
17use crate::context::Context;
18use crate::types::objects::EnumDefinition;
19
20pub fn generate(ctx: &Context, def: &EnumDefinition) -> TokenStream {
21 let enum_ = generate_enum(ctx, def);
22 let unknown = generate_unknown(ctx, def);
23
24 quote! {
25 #![allow(deprecated)]
27 use std::fmt;
28 use std::str;
29
30 #enum_
31 #unknown
32 }
33}
34
35fn generate_enum(ctx: &Context, def: &EnumDefinition) -> TokenStream {
36 let root_docs = ctx.docs(def.docs());
37 let name = ctx.type_name(def.type_name().name());
38 let result = ctx.result_ident(def.type_name());
39 let ok = ctx.ok_ident(def.type_name());
40 let err = ctx.err_ident(def.type_name());
41 let unknown = unknown(ctx, def);
42
43 let variants = def.values().iter().map(|v| {
44 let docs = ctx.docs(v.docs());
45 let deprecated = ctx.deprecated(v.deprecated());
46 let value = v.value();
47 let name = ctx.type_name(v.value());
48 quote! {
49 #docs
50 #deprecated
51 #[serde(rename = #value)]
52 #name,
53 }
54 });
55
56 let other_variant = if ctx.exhaustive() {
57 quote!()
58 } else {
59 quote! {
60 #[serde(untagged)]
62 #unknown(#unknown)
63 }
64 };
65
66 let as_str_arms = def.values().iter().map(|v| {
67 let value = v.value();
68 let variant = ctx.type_name(v.value());
69 let allow_deprecated = ctx.allow_deprecated(v.deprecated());
70 quote! {
71 #allow_deprecated
72 #name::#variant => #value,
73 }
74 });
75
76 let as_str_other = if ctx.exhaustive() {
77 quote!()
78 } else {
79 quote!(#name::#unknown(v) => &*v,)
80 };
81
82 let from_str_arms = def.values().iter().map(|v| {
83 let value = v.value();
84 let variant = ctx.type_name(value);
85 let allow_deprecated = ctx.allow_deprecated(v.deprecated());
86 quote! {
87 #allow_deprecated
88 #value => #ok(#name::#variant),
89 }
90 });
91
92 let from_str_other = if ctx.exhaustive() {
93 quote! {
94 _ => #err(conjure_object::plain::ParseEnumError::new()),
95 }
96 } else {
97 quote! {
98 v => v.parse().map(|v| #name::#unknown(#unknown(v))),
99 }
100 };
101
102 quote! {
103 #root_docs
104 #[derive(
105 Debug,
106 Clone,
107 PartialEq,
108 Eq,
109 PartialOrd,
110 Ord,
111 Hash,
112 conjure_object::serde::Deserialize,
113 conjure_object::serde::Serialize,
114 )]
115 #[serde(crate = "conjure_object::serde")]
116 pub enum #name {
117 #(#variants)*
118 #other_variant
119 }
120
121 impl #name {
122 #[inline]
124 pub fn as_str(&self) -> &str {
125 match self {
126 #(#as_str_arms)*
127 #as_str_other
128 }
129 }
130 }
131
132 impl fmt::Display for #name {
133 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
134 fmt::Display::fmt(self.as_str(), fmt)
135 }
136 }
137
138 impl conjure_object::Plain for #name {
139 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
140 conjure_object::Plain::fmt(self.as_str(), fmt)
141 }
142 }
143
144 impl str::FromStr for #name {
145 type Err = conjure_object::plain::ParseEnumError;
146
147 #[inline]
148 fn from_str(v: &str) -> #result<#name, conjure_object::plain::ParseEnumError> {
149 match v {
150 #(#from_str_arms)*
151 #from_str_other
152 }
153 }
154 }
155
156 impl conjure_object::FromPlain for #name {
157 type Err = conjure_object::plain::ParseEnumError;
158
159 #[inline]
160 fn from_plain(v: &str) -> #result<#name, conjure_object::plain::ParseEnumError> {
161 v.parse()
162 }
163 }
164 }
165}
166
167fn unknown(ctx: &Context, def: &EnumDefinition) -> TokenStream {
168 if ctx.type_name(def.type_name().name()) == "Unknown" {
169 quote!(Unknown_)
170 } else {
171 quote!(Unknown)
172 }
173}
174
175fn generate_unknown(ctx: &Context, def: &EnumDefinition) -> TokenStream {
176 if ctx.exhaustive() {
177 return quote!();
178 }
179
180 let doc = format!(
181 "An unknown variant of the {} enum.",
182 ctx.type_name(def.type_name().name())
183 );
184
185 let unknown = unknown(ctx, def);
186
187 quote! {
188 #[doc = #doc]
189 #[derive(
190 Debug,
191 Clone,
192 PartialEq,
193 Eq,
194 PartialOrd,
195 Ord,
196 Hash,
197 conjure_object::serde::Deserialize,
198 conjure_object::serde::Serialize,
199 )]
200 #[serde(crate = "conjure_object::serde", transparent)]
201 pub struct #unknown(conjure_object::private::Variant);
202
203 impl std::ops::Deref for #unknown {
204 type Target = str;
205
206 #[inline]
207 fn deref(&self) -> &str {
208 &self.0
209 }
210 }
211
212 impl fmt::Display for #unknown {
213 #[inline]
214 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
215 fmt::Display::fmt(&self.0, fmt)
216 }
217 }
218 }
219}