Skip to main content

conjure_codegen/
enums.rs

1// Copyright 2018 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14use 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        // https://github.com/serde-rs/serde/issues/2195
26        #![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            /// An unknown variant.
61            #[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            /// Returns the string representation of the enum.
123            #[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}