Skip to main content

conjure_codegen/
context.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.
14#![allow(clippy::match_like_matches_macro)]
15
16use heck::{ToSnakeCase, ToUpperCamelCase};
17use proc_macro2::{Ident, Span, TokenStream};
18use quote::quote;
19use std::cell::{Cell, RefCell};
20use std::collections::HashMap;
21
22use crate::errors::error_object_definition;
23use crate::types::objects::{
24    ArgumentDefinition, ConjureDefinition, Documentation, LogSafety, PrimitiveType, Type,
25    TypeDefinition, TypeName,
26};
27
28#[derive(Copy, Clone)]
29pub enum BaseModule {
30    Objects,
31    Errors,
32    Endpoints,
33    Clients,
34}
35
36impl BaseModule {
37    fn module(&self) -> String {
38        match self {
39            BaseModule::Objects => "objects".to_string(),
40            BaseModule::Errors => "errors".to_string(),
41            BaseModule::Endpoints => "endpoints".to_string(),
42            BaseModule::Clients => "clients".to_string(),
43        }
44    }
45}
46
47enum CachedLogSafety {
48    Uncomputed,
49    Computed(Option<LogSafety>),
50}
51
52struct TypeContext {
53    def: TypeDefinition,
54    has_double: Cell<Option<bool>>,
55    is_copy: Cell<Option<bool>>,
56    log_safety: RefCell<CachedLogSafety>,
57}
58
59pub struct Context {
60    types: HashMap<TypeName, TypeContext>,
61    exhaustive: bool,
62    serialize_empty_collections: bool,
63    use_legacy_error_serialization: bool,
64    public_fields: bool,
65    strip_prefix: Vec<String>,
66    version: Option<String>,
67}
68
69impl Context {
70    pub fn new(
71        defs: &ConjureDefinition,
72        exhaustive: bool,
73        serialize_empty_collections: bool,
74        use_legacy_error_serialization: bool,
75        public_fields: bool,
76        strip_prefix: Option<&str>,
77        version: Option<&str>,
78    ) -> Context {
79        let mut context = Context {
80            types: HashMap::new(),
81            exhaustive,
82            serialize_empty_collections,
83            use_legacy_error_serialization,
84            public_fields,
85            strip_prefix: vec![],
86            version: version.map(str::to_owned),
87        };
88
89        if let Some(strip_prefix) = strip_prefix {
90            context.strip_prefix = context.raw_module_path(strip_prefix);
91        }
92
93        for def in defs.types() {
94            let name = match &def {
95                TypeDefinition::Alias(def) => def.type_name().clone(),
96                TypeDefinition::Enum(def) => def.type_name().clone(),
97                TypeDefinition::Object(def) => def.type_name().clone(),
98                TypeDefinition::Union(def) => def.type_name().clone(),
99            };
100
101            context.types.insert(
102                name,
103                TypeContext {
104                    def: def.clone(),
105                    has_double: Cell::new(None),
106                    is_copy: Cell::new(None),
107                    log_safety: RefCell::new(CachedLogSafety::Uncomputed),
108                },
109            );
110        }
111
112        for def in defs.errors() {
113            context.types.insert(
114                def.error_name().clone(),
115                TypeContext {
116                    def: TypeDefinition::Object(error_object_definition(def)),
117                    has_double: Cell::new(None),
118                    is_copy: Cell::new(None),
119                    log_safety: RefCell::new(CachedLogSafety::Uncomputed),
120                },
121            );
122        }
123
124        context
125    }
126
127    pub fn exhaustive(&self) -> bool {
128        self.exhaustive
129    }
130
131    pub fn serialize_empty_collections(&self) -> bool {
132        self.serialize_empty_collections
133    }
134
135    pub fn use_legacy_error_serialization(&self) -> bool {
136        self.use_legacy_error_serialization
137    }
138
139    pub fn public_fields(&self) -> bool {
140        self.public_fields
141    }
142
143    fn needs_box(&self, def: &Type) -> bool {
144        match def {
145            Type::Primitive(_) => false,
146            Type::Optional(def) => self.needs_box(def.item_type()),
147            Type::List(_) | Type::Set(_) | Type::Map(_) => false,
148            Type::Reference(def) => self.ref_needs_box(def),
149            Type::External(def) => self.needs_box(def.fallback()),
150        }
151    }
152
153    fn ref_needs_box(&self, name: &TypeName) -> bool {
154        let ctx = &self.types[name];
155
156        match &ctx.def {
157            TypeDefinition::Alias(def) => self.needs_box(def.alias()),
158            TypeDefinition::Enum(_) => false,
159            TypeDefinition::Object(_) | TypeDefinition::Union(_) => true,
160        }
161    }
162
163    pub fn has_double(&self, def: &Type) -> bool {
164        match def {
165            Type::Primitive(def) => match *def {
166                PrimitiveType::Double => true,
167                _ => false,
168            },
169            Type::Optional(def) => self.has_double(def.item_type()),
170            Type::List(def) => self.has_double(def.item_type()),
171            Type::Set(def) => self.has_double(def.item_type()),
172            Type::Map(def) => self.has_double(def.key_type()) || self.has_double(def.value_type()),
173            Type::Reference(def) => self.ref_has_double(def),
174            Type::External(def) => self.has_double(def.fallback()),
175        }
176    }
177
178    fn ref_has_double(&self, name: &TypeName) -> bool {
179        let ctx = &self.types[name];
180
181        if let Some(has_double) = ctx.has_double.get() {
182            return has_double;
183        }
184
185        ctx.has_double.set(Some(false)); // break cycles
186        let has_double = match &ctx.def {
187            TypeDefinition::Alias(def) => self.has_double(def.alias()),
188            TypeDefinition::Enum(_) => false,
189            TypeDefinition::Object(def) => def.fields().iter().any(|f| self.has_double(f.type_())),
190            TypeDefinition::Union(def) => def.union_().iter().any(|f| self.has_double(f.type_())),
191        };
192
193        ctx.has_double.set(Some(has_double));
194        has_double
195    }
196
197    pub fn is_copy(&self, def: &Type) -> bool {
198        match def {
199            Type::Primitive(def) => match *def {
200                PrimitiveType::String
201                | PrimitiveType::Binary
202                | PrimitiveType::Any
203                | PrimitiveType::Rid
204                | PrimitiveType::Bearertoken => false,
205                PrimitiveType::Datetime
206                | PrimitiveType::Integer
207                | PrimitiveType::Double
208                | PrimitiveType::Safelong
209                | PrimitiveType::Boolean
210                | PrimitiveType::Uuid => true,
211            },
212            Type::Optional(def) => self.is_copy(def.item_type()),
213            Type::List(_) | Type::Set(_) | Type::Map(_) => false,
214            Type::Reference(def) => self.ref_is_copy(def),
215            Type::External(def) => self.is_copy(def.fallback()),
216        }
217    }
218
219    fn ref_is_copy(&self, name: &TypeName) -> bool {
220        let ctx = &self.types[name];
221
222        if let Some(is_copy) = ctx.is_copy.get() {
223            return is_copy;
224        }
225
226        let is_copy = match &ctx.def {
227            TypeDefinition::Alias(def) => self.is_copy(def.alias()),
228            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => false,
229        };
230
231        ctx.is_copy.set(Some(is_copy));
232        is_copy
233    }
234
235    pub fn is_required(&self, def: &Type) -> bool {
236        match def {
237            Type::Primitive(_) => true,
238            Type::Optional(_) | Type::List(_) | Type::Set(_) | Type::Map(_) => false,
239            Type::Reference(def) => self.ref_is_required(def),
240            Type::External(def) => self.is_required(def.fallback()),
241        }
242    }
243
244    fn ref_is_required(&self, name: &TypeName) -> bool {
245        let ctx = &self.types[name];
246
247        match &ctx.def {
248            TypeDefinition::Alias(def) => self.is_required(def.alias()),
249            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => true,
250        }
251    }
252
253    pub fn is_default(&self, def: &Type) -> bool {
254        match def {
255            Type::Primitive(def) => match *def {
256                PrimitiveType::String
257                | PrimitiveType::Integer
258                | PrimitiveType::Double
259                | PrimitiveType::Safelong
260                | PrimitiveType::Binary
261                | PrimitiveType::Boolean => true,
262                PrimitiveType::Datetime
263                | PrimitiveType::Any
264                | PrimitiveType::Uuid
265                | PrimitiveType::Rid
266                | PrimitiveType::Bearertoken => false,
267            },
268            Type::Optional(_) | Type::List(_) | Type::Set(_) | Type::Map(_) => true,
269            Type::Reference(def) => self.ref_is_default(def),
270            Type::External(def) => self.is_default(def.fallback()),
271        }
272    }
273
274    fn ref_is_default(&self, name: &TypeName) -> bool {
275        let ctx = &self.types[name];
276
277        match &ctx.def {
278            TypeDefinition::Alias(def) => self.is_default(def.alias()),
279            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => false,
280        }
281    }
282
283    pub fn is_display(&self, def: &Type) -> bool {
284        match def {
285            Type::Primitive(def) => match *def {
286                PrimitiveType::String
287                | PrimitiveType::Datetime
288                | PrimitiveType::Integer
289                | PrimitiveType::Double
290                | PrimitiveType::Safelong
291                | PrimitiveType::Boolean
292                | PrimitiveType::Uuid
293                | PrimitiveType::Rid => true,
294                PrimitiveType::Binary | PrimitiveType::Any | PrimitiveType::Bearertoken => false,
295            },
296            Type::Optional(_) | Type::List(_) | Type::Set(_) | Type::Map(_) => false,
297            Type::Reference(def) => self.ref_is_display(def),
298            Type::External(def) => self.is_display(def.fallback()),
299        }
300    }
301
302    fn ref_is_display(&self, name: &TypeName) -> bool {
303        match &self.types[name].def {
304            TypeDefinition::Alias(def) => self.is_display(def.alias()),
305            TypeDefinition::Enum(_) => true,
306            TypeDefinition::Object(_) | TypeDefinition::Union(_) => false,
307        }
308    }
309
310    pub fn is_from_iter(
311        &self,
312        base_module: BaseModule,
313        this_type: &TypeName,
314        def: &Type,
315    ) -> Option<TokenStream> {
316        match def {
317            Type::Primitive(_) | Type::Optional(_) => None,
318            Type::Map(def) => {
319                let key = self.rust_type_inner(base_module, this_type, def.key_type(), true);
320                let value = self.rust_type(base_module, this_type, def.value_type());
321                Some(quote!((#key, #value)))
322            }
323            Type::List(def) => Some(self.rust_type(base_module, this_type, def.item_type())),
324            Type::Set(def) => {
325                Some(self.rust_type_inner(base_module, this_type, def.item_type(), true))
326            }
327            Type::Reference(def) => self.ref_is_from_iter(base_module, this_type, def),
328            Type::External(def) => self.is_from_iter(base_module, this_type, def.fallback()),
329        }
330    }
331
332    fn ref_is_from_iter(
333        &self,
334        base_module: BaseModule,
335        this_type: &TypeName,
336        name: &TypeName,
337    ) -> Option<TokenStream> {
338        match &self.types[name].def {
339            TypeDefinition::Alias(def) => self.is_from_iter(base_module, this_type, def.alias()),
340            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => None,
341        }
342    }
343
344    pub fn dealiased_type<'a>(&'a self, def: &'a Type) -> &'a Type {
345        match def {
346            Type::Primitive(_)
347            | Type::Optional(_)
348            | Type::List(_)
349            | Type::Set(_)
350            | Type::Map(_) => def,
351            Type::Reference(name) => match &self.types[name].def {
352                TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => {
353                    def
354                }
355                TypeDefinition::Alias(def) => self.dealiased_type(def.alias()),
356            },
357            Type::External(def) => self.dealiased_type(def.fallback()),
358        }
359    }
360
361    pub fn is_aliased(&self, def: &Type) -> bool {
362        match def {
363            Type::Reference(name) => matches!(&self.types[name].def, TypeDefinition::Alias(_)),
364            Type::External(ext) => self.is_aliased(ext.fallback()),
365            _ => false,
366        }
367    }
368
369    pub fn rust_type(
370        &self,
371        base_module: BaseModule,
372        this_type: &TypeName,
373        def: &Type,
374    ) -> TokenStream {
375        self.rust_type_inner(base_module, this_type, def, false)
376    }
377
378    fn rust_type_inner(
379        &self,
380        base_module: BaseModule,
381        this_type: &TypeName,
382        def: &Type,
383        key: bool,
384    ) -> TokenStream {
385        match def {
386            Type::Primitive(def) => match *def {
387                PrimitiveType::String => self.string_ident(this_type),
388                PrimitiveType::Datetime => quote!(conjure_object::DateTime<conjure_object::Utc>),
389                PrimitiveType::Integer => quote!(i32),
390                PrimitiveType::Double => {
391                    if key {
392                        quote!(conjure_object::DoubleKey)
393                    } else {
394                        quote!(f64)
395                    }
396                }
397                PrimitiveType::Safelong => quote!(conjure_object::SafeLong),
398                PrimitiveType::Binary => quote!(conjure_object::Bytes),
399                PrimitiveType::Any => quote!(conjure_object::Any),
400                PrimitiveType::Boolean => quote!(bool),
401                PrimitiveType::Uuid => quote!(conjure_object::Uuid),
402                PrimitiveType::Rid => quote!(conjure_object::ResourceIdentifier),
403                PrimitiveType::Bearertoken => quote!(conjure_object::BearerToken),
404            },
405            Type::Optional(def) => {
406                let option = self.option_ident(this_type);
407                let item = self.rust_type_inner(base_module, this_type, def.item_type(), key);
408                quote!(#option<#item>)
409            }
410            Type::List(def) => {
411                let vec = self.vec_ident(this_type);
412                let item = self.rust_type_inner(base_module, this_type, def.item_type(), key);
413                quote!(#vec<#item>)
414            }
415            Type::Set(def) => {
416                let item = self.rust_type_inner(base_module, this_type, def.item_type(), true);
417                quote!(std::collections::BTreeSet<#item>)
418            }
419            Type::Map(def) => {
420                let key = self.rust_type_inner(base_module, this_type, def.key_type(), true);
421                let value = self.rust_type(base_module, this_type, def.value_type());
422                quote!(std::collections::BTreeMap<#key, #value>)
423            }
424            Type::Reference(def) => self.type_path(base_module, this_type, def),
425            Type::External(def) => {
426                self.rust_type_inner(base_module, this_type, def.fallback(), key)
427            }
428        }
429    }
430
431    pub fn boxed_rust_type(
432        &self,
433        base_module: BaseModule,
434        this_type: &TypeName,
435        def: &Type,
436    ) -> TokenStream {
437        match def {
438            Type::Optional(def) => {
439                let option = self.option_ident(this_type);
440                let item = self.boxed_rust_type(base_module, this_type, def.item_type());
441                quote!(#option<#item>)
442            }
443            Type::Reference(def) => self.ref_boxed_rust_type(base_module, this_type, def),
444            Type::External(def) => self.boxed_rust_type(base_module, this_type, def.fallback()),
445            def => self.rust_type(base_module, this_type, def),
446        }
447    }
448
449    fn ref_boxed_rust_type(
450        &self,
451        base_module: BaseModule,
452        this_type: &TypeName,
453        name: &TypeName,
454    ) -> TokenStream {
455        let ctx = &self.types[name];
456
457        let needs_box = match &ctx.def {
458            TypeDefinition::Alias(def) => self.needs_box(def.alias()),
459            TypeDefinition::Enum(_) => false,
460            TypeDefinition::Object(_) => match &self.types[this_type].def {
461                TypeDefinition::Union(_) => false,
462                _ => true,
463            },
464            TypeDefinition::Union(_) => true,
465        };
466
467        let unboxed = self.type_path(base_module, this_type, name);
468        if needs_box {
469            let box_ = self.box_ident(name);
470            quote!(#box_<#unboxed>)
471        } else {
472            unboxed
473        }
474    }
475
476    pub fn borrowed_rust_type(
477        &self,
478        base_module: BaseModule,
479        this_type: &TypeName,
480        def: &Type,
481    ) -> TokenStream {
482        match def {
483            Type::Primitive(def) => match *def {
484                PrimitiveType::String => quote!(&str),
485                PrimitiveType::Datetime => quote!(conjure_object::DateTime<conjure_object::Utc>),
486                PrimitiveType::Integer => quote!(i32),
487                PrimitiveType::Double => quote!(f64),
488                PrimitiveType::Safelong => quote!(conjure_object::SafeLong),
489                PrimitiveType::Binary => quote!(&conjure_object::Bytes),
490                PrimitiveType::Any => quote!(&conjure_object::Any),
491                PrimitiveType::Boolean => quote!(bool),
492                PrimitiveType::Uuid => quote!(conjure_object::Uuid),
493                PrimitiveType::Rid => quote!(&conjure_object::ResourceIdentifier),
494                PrimitiveType::Bearertoken => quote!(&conjure_object::BearerToken),
495            },
496            Type::Optional(def) => {
497                let option = self.option_ident(this_type);
498                let item = self.borrowed_rust_type(base_module, this_type, def.item_type());
499                quote!(#option<#item>)
500            }
501            Type::List(def) => {
502                let item = self.rust_type(base_module, this_type, def.item_type());
503                quote!(&[#item])
504            }
505            Type::Set(def) => {
506                let item = self.rust_type_inner(base_module, this_type, def.item_type(), true);
507                quote!(&std::collections::BTreeSet<#item>)
508            }
509            Type::Map(def) => {
510                let key = self.rust_type_inner(base_module, this_type, def.key_type(), true);
511                let value = self.rust_type(base_module, this_type, def.value_type());
512                quote!(&std::collections::BTreeMap<#key, #value>)
513            }
514            Type::Reference(def) => self.borrowed_rust_type_ref(base_module, this_type, def),
515            Type::External(def) => self.borrowed_rust_type(base_module, this_type, def.fallback()),
516        }
517    }
518
519    fn borrowed_rust_type_ref(
520        &self,
521        base_module: BaseModule,
522        this_type: &TypeName,
523        name: &TypeName,
524    ) -> TokenStream {
525        let ctx = &self.types[name];
526
527        let type_ = self.type_path(base_module, this_type, name);
528        match &ctx.def {
529            TypeDefinition::Alias(def) => {
530                if self.is_copy(def.alias()) {
531                    type_
532                } else {
533                    quote!(&#type_)
534                }
535            }
536            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => {
537                quote!(&#type_)
538            }
539        }
540    }
541
542    pub fn borrow_rust_type(&self, value: TokenStream, def: &Type) -> TokenStream {
543        match def {
544            Type::Primitive(def) => match *def {
545                PrimitiveType::String => quote!(&*#value),
546                PrimitiveType::Any
547                | PrimitiveType::Rid
548                | PrimitiveType::Bearertoken
549                | PrimitiveType::Binary => {
550                    quote!(&#value)
551                }
552                PrimitiveType::Datetime
553                | PrimitiveType::Integer
554                | PrimitiveType::Double
555                | PrimitiveType::Safelong
556                | PrimitiveType::Boolean
557                | PrimitiveType::Uuid => value,
558            },
559            Type::Optional(def) => {
560                let borrow_item = self.borrow_rust_type(quote!(*o), def.item_type());
561                quote!(#value.as_ref().map(|o| #borrow_item))
562            }
563            Type::List(_) => quote!(&*#value),
564            Type::Set(_) | Type::Map(_) => quote!(&#value),
565            Type::Reference(def) => self.borrow_rust_type_ref(value, def),
566            Type::External(def) => self.borrow_rust_type(value, def.fallback()),
567        }
568    }
569
570    fn borrow_rust_type_ref(&self, value: TokenStream, name: &TypeName) -> TokenStream {
571        let ctx = &self.types[name];
572
573        match &ctx.def {
574            TypeDefinition::Alias(def) => {
575                if self.needs_box(def.alias()) {
576                    quote!(&*#value)
577                } else if self.is_copy(def.alias()) {
578                    value
579                } else {
580                    quote!(&#value)
581                }
582            }
583            TypeDefinition::Enum(_) => quote!(&#value),
584            TypeDefinition::Object(_) | TypeDefinition::Union(_) => quote!(&*#value),
585        }
586    }
587
588    pub fn builder_config(
589        &self,
590        base_module: BaseModule,
591        this_type: &TypeName,
592        def: &Type,
593    ) -> BuilderConfig {
594        match def {
595            Type::Primitive(def) => match def {
596                PrimitiveType::String | PrimitiveType::Binary => BuilderConfig::Into,
597                PrimitiveType::Any => BuilderConfig::Custom {
598                    type_: quote!(impl conjure_object::serde::Serialize),
599                    convert: quote!(
600                        |v| conjure_object::Any::new(v).expect("value failed to serialize")
601                    ),
602                },
603                _ => BuilderConfig::Normal,
604            },
605            Type::Optional(def) => {
606                if self.needs_box(def.item_type()) {
607                    let into = self.into_ident(this_type);
608                    let option = self.option_ident(this_type);
609                    let item_type = self.rust_type(base_module, this_type, def.item_type());
610                    let box_ = self.box_ident(this_type);
611                    BuilderConfig::Custom {
612                        type_: quote!(impl #into<#option<#item_type>>),
613                        convert: quote!(|v| v.into().map(#box_::new)),
614                    }
615                } else {
616                    BuilderConfig::Into
617                }
618            }
619            Type::List(def) => BuilderConfig::List {
620                item: self.builder_item_config(base_module, this_type, def.item_type(), false),
621            },
622            Type::Set(def) => BuilderConfig::Set {
623                item: self.builder_item_config(base_module, this_type, def.item_type(), true),
624            },
625            Type::Map(def) => BuilderConfig::Map {
626                key: self.builder_item_config(base_module, this_type, def.key_type(), true),
627                value: self.builder_item_config(base_module, this_type, def.value_type(), false),
628            },
629            Type::Reference(def) => {
630                if self.ref_needs_box(def) {
631                    let box_ = self.box_ident(this_type);
632                    BuilderConfig::Custom {
633                        type_: self.type_path(base_module, this_type, def),
634                        convert: quote!(#box_::new),
635                    }
636                } else {
637                    BuilderConfig::Normal
638                }
639            }
640            Type::External(def) => self.builder_config(base_module, this_type, def.fallback()),
641        }
642    }
643
644    fn builder_item_config(
645        &self,
646        base_module: BaseModule,
647        this_type: &TypeName,
648        def: &Type,
649        key: bool,
650    ) -> BuilderItemConfig {
651        match def {
652            Type::Primitive(primitive) => match primitive {
653                PrimitiveType::String => BuilderItemConfig::Into {
654                    type_: self.string_ident(this_type),
655                },
656                PrimitiveType::Binary => BuilderItemConfig::Into {
657                    type_: quote!(conjure_object::Bytes),
658                },
659                PrimitiveType::Any => BuilderItemConfig::Custom {
660                    type_: quote!(impl conjure_object::serde::Serialize),
661                    convert: quote!(
662                        |v| conjure_object::Any::new(v).expect("value failed to serialize")
663                    ),
664                },
665                _ => BuilderItemConfig::Normal {
666                    type_: self.rust_type_inner(base_module, this_type, def, key),
667                },
668            },
669            Type::Optional(def) => {
670                let option = self.option_ident(this_type);
671                let item_type = self.rust_type(base_module, this_type, def.item_type());
672                BuilderItemConfig::Into {
673                    type_: quote!(#option<#item_type>),
674                }
675            }
676            Type::List(def) => {
677                let into_iterator = self.into_iterator_ident(this_type);
678                let item_type = self.rust_type_inner(base_module, this_type, def.item_type(), key);
679                BuilderItemConfig::Custom {
680                    type_: quote!(impl #into_iterator<Item = #item_type>),
681                    convert: quote!(|v| v.into_iter().collect()),
682                }
683            }
684            Type::Set(def) => {
685                let into_iterator = self.into_iterator_ident(this_type);
686                let item_type = self.rust_type_inner(base_module, this_type, def.item_type(), true);
687                BuilderItemConfig::Custom {
688                    type_: quote!(impl #into_iterator<Item = #item_type>),
689                    convert: quote!(|v| v.into_iter().collect()),
690                }
691            }
692            Type::Map(def) => {
693                let into_iterator = self.into_iterator_ident(this_type);
694                let key_type = self.rust_type_inner(base_module, this_type, def.key_type(), true);
695                let value_type = self.rust_type(base_module, this_type, def.value_type());
696                BuilderItemConfig::Custom {
697                    type_: quote!(impl #into_iterator<Item = (#key_type, #value_type)>),
698                    convert: quote!(|v| v.into_iter().collect()),
699                }
700            }
701            Type::Reference(def) => BuilderItemConfig::Normal {
702                type_: self.type_path(base_module, this_type, def),
703            },
704            Type::External(def) => {
705                self.builder_item_config(base_module, this_type, def.fallback(), key)
706            }
707        }
708    }
709
710    pub fn is_empty_method(&self, this_type: &TypeName, def: &Type) -> Option<String> {
711        match def {
712            Type::Primitive(_) => None,
713            Type::Optional(_) => {
714                let option = self.option_ident(this_type);
715                Some(format!("{option}::is_none"))
716            }
717            Type::List(_) => {
718                let vec = self.vec_ident(this_type);
719                Some(format!("{vec}::is_empty"))
720            }
721            Type::Set(_) => Some("std::collections::BTreeSet::is_empty".to_string()),
722            Type::Map(_) => Some("std::collections::BTreeMap::is_empty".to_string()),
723            Type::Reference(def) => self.is_empty_method_ref(this_type, def),
724            Type::External(def) => self.is_empty_method(this_type, def.fallback()),
725        }
726    }
727
728    fn is_empty_method_ref(&self, this_type: &TypeName, name: &TypeName) -> Option<String> {
729        let ctx = &self.types[name];
730
731        match &ctx.def {
732            TypeDefinition::Alias(def) => self.is_empty_method(this_type, def.alias()),
733            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => None,
734        }
735    }
736
737    pub fn is_binary(&self, def: &Type) -> bool {
738        match def {
739            Type::Primitive(PrimitiveType::Binary) => true,
740            Type::Primitive(_)
741            | Type::Optional(_)
742            | Type::List(_)
743            | Type::Set(_)
744            | Type::Map(_) => false,
745            Type::Reference(def) => self.is_binary_ref(def),
746            Type::External(def) => self.is_binary(def.fallback()),
747        }
748    }
749
750    fn is_binary_ref(&self, name: &TypeName) -> bool {
751        let ctx = &self.types[name];
752
753        match &ctx.def {
754            TypeDefinition::Alias(def) => self.is_binary(def.alias()),
755            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => false,
756        }
757    }
758
759    pub fn is_plain(&self, def: &Type) -> bool {
760        match def {
761            Type::Primitive(primitive) => match primitive {
762                PrimitiveType::String
763                | PrimitiveType::Datetime
764                | PrimitiveType::Integer
765                | PrimitiveType::Double
766                | PrimitiveType::Safelong
767                | PrimitiveType::Binary
768                | PrimitiveType::Boolean
769                | PrimitiveType::Uuid
770                | PrimitiveType::Rid
771                | PrimitiveType::Bearertoken => true,
772                PrimitiveType::Any => false,
773            },
774            Type::Optional(_) | Type::List(_) | Type::Set(_) | Type::Map(_) => false,
775            Type::Reference(def) => self.is_plain_ref(def),
776            Type::External(def) => self.is_plain(def.fallback()),
777        }
778    }
779
780    fn is_plain_ref(&self, name: &TypeName) -> bool {
781        let ctx = &self.types[name];
782
783        match &ctx.def {
784            TypeDefinition::Alias(def) => self.is_plain(def.alias()),
785            TypeDefinition::Enum(_) => true,
786            TypeDefinition::Object(_) | TypeDefinition::Union(_) => false,
787        }
788    }
789
790    pub fn is_iterable(&self, def: &Type) -> bool {
791        match def {
792            Type::Primitive(_) => false,
793            Type::Optional(_) | Type::List(_) | Type::Set(_) | Type::Map(_) => true,
794            Type::Reference(def) => self.is_iterable_ref(def),
795            Type::External(def) => self.is_iterable(def.fallback()),
796        }
797    }
798
799    fn is_iterable_ref(&self, name: &TypeName) -> bool {
800        let ctx = &self.types[name];
801
802        match &ctx.def {
803            TypeDefinition::Alias(def) => self.is_iterable(def.alias()),
804            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => false,
805        }
806    }
807
808    pub fn is_optional<'a>(&'a self, def: &'a Type) -> Option<&'a Type> {
809        match def {
810            Type::Primitive(_) | Type::List(_) | Type::Set(_) | Type::Map(_) => None,
811            Type::Optional(def) => Some(def.item_type()),
812            Type::Reference(def) => self.is_optional_ref(def),
813            Type::External(def) => self.is_optional(def.fallback()),
814        }
815    }
816
817    fn is_optional_ref(&self, name: &TypeName) -> Option<&Type> {
818        let ctx = &self.types[name];
819
820        match &ctx.def {
821            TypeDefinition::Alias(def) => self.is_optional(def.alias()),
822            TypeDefinition::Enum(_) | TypeDefinition::Object(_) | TypeDefinition::Union(_) => None,
823        }
824    }
825
826    #[allow(clippy::only_used_in_recursion)]
827    pub fn is_double(&self, def: &Type) -> bool {
828        match def {
829            Type::Primitive(PrimitiveType::Double) => true,
830            Type::Optional(def) => self.is_double(def.item_type()),
831            Type::List(def) => self.is_double(def.item_type()),
832            Type::Map(def) => self.is_double(def.value_type()),
833            Type::Primitive(_) | Type::Set(_) | Type::Reference(_) => false,
834            Type::External(def) => self.is_double(def.fallback()),
835        }
836    }
837
838    pub fn docs(&self, docs: Option<&Documentation>) -> TokenStream {
839        match docs {
840            Some(docs) => {
841                let docs = docs
842                    .lines()
843                    // Add a leading space to align with standard doc comments.
844                    .map(|s| format!(" {s}"))
845                    // Docs may have code blocks, which will turn into rust doctests by default,
846                    // and probably not build. Sticking a `ignore` info string onto the opening
847                    // fence makes rustdoc skip them.
848                    .map({
849                        let mut in_code_block = false;
850                        move |mut s| {
851                            if !s.trim().starts_with("```") {
852                                return s;
853                            }
854
855                            if in_code_block {
856                                in_code_block = false;
857                                return s;
858                            }
859
860                            if s.trim() == "```" {
861                                s.push_str("ignore");
862                            }
863                            in_code_block = true;
864
865                            s
866                        }
867                    });
868                quote!(#(#[doc = #docs])*)
869            }
870            None => quote!(),
871        }
872    }
873
874    pub fn deprecated(&self, deprecated: Option<&Documentation>) -> TokenStream {
875        match deprecated {
876            Some(docs) => {
877                let docs = &**docs;
878                quote!(#[deprecated(note = #docs)])
879            }
880            None => quote!(),
881        }
882    }
883
884    pub fn allow_deprecated(&self, deprecated: Option<&Documentation>) -> TokenStream {
885        match deprecated {
886            Some(_) => quote!(#[allow(deprecated)]),
887            None => quote!(),
888        }
889    }
890
891    pub fn box_ident(&self, name: &TypeName) -> TokenStream {
892        self.prelude_ident(name, "Box", "std::boxed::Box")
893    }
894
895    pub fn result_ident(&self, name: &TypeName) -> TokenStream {
896        self.prelude_ident(name, "Result", "std::result::Result")
897    }
898
899    pub fn ok_ident(&self, name: &TypeName) -> TokenStream {
900        self.prelude_ident(name, "Ok", "Result::Ok")
901    }
902
903    pub fn err_ident(&self, name: &TypeName) -> TokenStream {
904        self.prelude_ident(name, "Err", "Result::Err")
905    }
906
907    pub fn option_ident(&self, name: &TypeName) -> TokenStream {
908        self.prelude_ident(name, "Option", "std::option::Option")
909    }
910
911    pub fn some_ident(&self, name: &TypeName) -> TokenStream {
912        self.prelude_ident(name, "Some", "Option::Some")
913    }
914
915    pub fn none_ident(&self, name: &TypeName) -> TokenStream {
916        self.prelude_ident(name, "None", "Option::None")
917    }
918
919    pub fn string_ident(&self, name: &TypeName) -> TokenStream {
920        self.prelude_ident(name, "String", "std::string::String")
921    }
922
923    pub fn vec_ident(&self, name: &TypeName) -> TokenStream {
924        self.prelude_ident(name, "Vec", "std::vec::Vec")
925    }
926
927    #[allow(clippy::wrong_self_convention)]
928    pub fn into_ident(&self, name: &TypeName) -> TokenStream {
929        self.prelude_ident(name, "Into", "std::convert::Into")
930    }
931
932    #[allow(clippy::wrong_self_convention)]
933    pub fn into_iterator_ident(&self, name: &TypeName) -> TokenStream {
934        self.prelude_ident(name, "IntoIterator", "std::iter::IntoIterator")
935    }
936
937    pub fn iterator_ident(&self, name: &TypeName) -> TokenStream {
938        self.prelude_ident(name, "Iterator", "std::iter::Iterator")
939    }
940
941    pub fn sync_ident(&self, name: &TypeName) -> TokenStream {
942        self.prelude_ident(name, "Sync", "std::marker::Sync")
943    }
944
945    pub fn send_ident(&self, name: &TypeName) -> TokenStream {
946        self.prelude_ident(name, "Send", "std::marker::Send")
947    }
948
949    fn prelude_ident(&self, name: &TypeName, short: &str, long: &str) -> TokenStream {
950        let s = if self.type_name(name.name()) == short {
951            long
952        } else {
953            short
954        };
955
956        s.parse().unwrap()
957    }
958
959    pub fn module_name(&self, name: &TypeName) -> String {
960        self.ident_name(name.name())
961    }
962
963    pub fn field_name(&self, s: &str) -> Ident {
964        Ident::new(&self.ident_name(s), Span::call_site())
965    }
966
967    fn ident_name(&self, s: &str) -> String {
968        let mut s = s.to_snake_case();
969
970        let keyword = match &*s {
971            // strict keywords
972            "as" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern"
973            | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod"
974            | "move" | "mut" | "pub" | "ref" | "return" | "self" | "static" | "struct"
975            | "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" | "while" => true,
976            // reserved keywords
977            "abstract" | "async" | "become" | "box" | "do" | "final" | "macro" | "override"
978            | "priv" | "typeof" | "unsized" | "virtual" | "yield" => true,
979            // weak keywords
980            "union" | "dyn" => true,
981            // builder pattern methods
982            "build" | "builder" | "new" => true,
983            _ => false,
984        };
985
986        if keyword {
987            s.push('_');
988        }
989
990        s
991    }
992
993    pub fn type_name(&self, name: &str) -> Ident {
994        let mut name = name.to_upper_camel_case();
995
996        let keyword = match &*name {
997            "Self" => true,
998            _ => false,
999        };
1000
1001        if keyword {
1002            name.push('_');
1003        }
1004
1005        Ident::new(&name, Span::call_site())
1006    }
1007
1008    pub fn module_path(&self, base: BaseModule, name: &TypeName) -> Vec<String> {
1009        let raw = self.raw_module_path(name.package());
1010
1011        let mut stripped = if raw.starts_with(&self.strip_prefix) {
1012            raw[self.strip_prefix.len()..].to_vec()
1013        } else {
1014            raw
1015        };
1016
1017        stripped.insert(0, base.module());
1018        stripped
1019    }
1020
1021    fn raw_module_path(&self, package: &str) -> Vec<String> {
1022        package.split('.').map(|s| self.ident_name(s)).collect()
1023    }
1024
1025    fn type_path(
1026        &self,
1027        this_module: BaseModule,
1028        this_type: &TypeName,
1029        other_type: &TypeName,
1030    ) -> TokenStream {
1031        let this_module_path = self.module_path(this_module, this_type);
1032        let other_module_path = self.module_path(BaseModule::Objects, other_type);
1033
1034        let shared_prefix = this_module_path
1035            .iter()
1036            .zip(&other_module_path)
1037            .take_while(|(a, b)| a == b)
1038            .count();
1039
1040        // one super to get out of this type's module
1041        let mut components = vec![quote!(super)];
1042
1043        // one for each part of this type's unique module prefix
1044        for _ in 0..this_module_path.len() - shared_prefix {
1045            components.push(quote!(super));
1046        }
1047
1048        // then the path to the other type's module
1049        for component in &other_module_path[shared_prefix..] {
1050            components.push(component.parse().unwrap());
1051        }
1052
1053        let other_type_name = self.type_name(other_type.name());
1054
1055        quote!(#(#components::)* #other_type_name)
1056    }
1057
1058    // https://github.com/palantir/conjure-java/blob/develop/conjure-java-core/src/main/java/com/palantir/conjure/java/types/SafetyEvaluator.java
1059    pub fn is_safe_arg(&self, arg: &ArgumentDefinition) -> bool {
1060        if let Some(log_safety) = arg.safety() {
1061            return *log_safety == LogSafety::Safe;
1062        }
1063
1064        if self.is_legacy_safe_arg(arg) {
1065            return true;
1066        }
1067
1068        self.type_log_safety(arg.type_()) == Some(LogSafety::Safe)
1069    }
1070
1071    fn is_legacy_safe_arg(&self, arg: &ArgumentDefinition) -> bool {
1072        arg.tags().iter().any(|s| s == "safe")
1073            || arg.markers().iter().any(|a| self.is_legacy_safe_marker(a))
1074    }
1075
1076    fn is_legacy_safe_marker(&self, ty: &Type) -> bool {
1077        match ty {
1078            Type::External(def) => {
1079                let name = def.external_reference();
1080                name.package() == "com.palantir.logsafe" && name.name() == "Safe"
1081            }
1082            _ => false,
1083        }
1084    }
1085
1086    fn type_log_safety(&self, ty: &Type) -> Option<LogSafety> {
1087        match ty {
1088            Type::Primitive(primitive) => self.primitive_log_safety(primitive),
1089            Type::Optional(optional) => self.type_log_safety(optional.item_type()),
1090            Type::List(list) => self.type_log_safety(list.item_type()),
1091            Type::Set(set) => self.type_log_safety(set.item_type()),
1092            Type::Map(map) => self.combine_safety(
1093                self.type_log_safety(map.key_type()),
1094                self.type_log_safety(map.value_type()),
1095            ),
1096            Type::Reference(def) => self.type_log_safety_ref(def),
1097            Type::External(_) => None,
1098        }
1099    }
1100
1101    fn primitive_log_safety(&self, primitive: &PrimitiveType) -> Option<LogSafety> {
1102        match primitive {
1103            PrimitiveType::Bearertoken => Some(LogSafety::DoNotLog),
1104            _ => None,
1105        }
1106    }
1107
1108    fn type_log_safety_ref(&self, name: &TypeName) -> Option<LogSafety> {
1109        let ctx = &self.types[name];
1110
1111        if let CachedLogSafety::Computed(safety) = &*ctx.log_safety.borrow() {
1112            return safety.clone();
1113        }
1114
1115        // temporarily treat it as safe in case of recursive type definitions.
1116        *ctx.log_safety.borrow_mut() = CachedLogSafety::Computed(Some(LogSafety::Safe));
1117
1118        let safety = match &ctx.def {
1119            TypeDefinition::Alias(alias) => alias
1120                .safety()
1121                .cloned()
1122                .or_else(|| self.type_log_safety(alias.alias())),
1123            // We consider enums to be safe even when not compiled as exhaustive, since we assume
1124            // unknown variants are simply from a future definition.
1125            TypeDefinition::Enum(_) => Some(LogSafety::Safe),
1126            TypeDefinition::Object(object) => object
1127                .fields()
1128                .iter()
1129                .map(|f| {
1130                    f.safety()
1131                        .cloned()
1132                        .or_else(|| self.type_log_safety(f.type_()))
1133                })
1134                .try_fold(LogSafety::Safe, |a, b| self.combine_safety(Some(a), b)),
1135            TypeDefinition::Union(union_) => union_
1136                .union_()
1137                .iter()
1138                .map(|f| {
1139                    f.safety()
1140                        .cloned()
1141                        .or_else(|| self.type_log_safety(f.type_()))
1142                })
1143                // The unknown variant is unsafe to log, and we don't want log safety to vary based
1144                // on the type generation configuration. However, like conjure-java we're going to
1145                // treat the unknown variant as unannotated for now to ease the rollout.
1146                .fold(None, |a, b| self.combine_safety(a, b)),
1147        };
1148
1149        *ctx.log_safety.borrow_mut() = CachedLogSafety::Computed(safety.clone());
1150        safety
1151    }
1152
1153    fn combine_safety(&self, a: Option<LogSafety>, b: Option<LogSafety>) -> Option<LogSafety> {
1154        match (a, b) {
1155            (Some(LogSafety::DoNotLog), _) | (_, Some(LogSafety::DoNotLog)) => {
1156                Some(LogSafety::DoNotLog)
1157            }
1158            (Some(LogSafety::Unsafe), _) | (_, Some(LogSafety::Unsafe)) => Some(LogSafety::Unsafe),
1159            (Some(LogSafety::Safe), Some(LogSafety::Safe)) => Some(LogSafety::Safe),
1160            // nb: we notably do not combine safe + unknown to safe
1161            (Some(LogSafety::Safe), None) | (None, Some(LogSafety::Safe)) | (None, None) => None,
1162        }
1163    }
1164
1165    pub fn version(&self) -> Option<&str> {
1166        self.version.as_deref()
1167    }
1168}
1169
1170pub enum BuilderConfig {
1171    Normal,
1172    Into,
1173    Custom {
1174        type_: TokenStream,
1175        convert: TokenStream,
1176    },
1177    List {
1178        item: BuilderItemConfig,
1179    },
1180    Set {
1181        item: BuilderItemConfig,
1182    },
1183    Map {
1184        key: BuilderItemConfig,
1185        value: BuilderItemConfig,
1186    },
1187}
1188
1189pub enum BuilderItemConfig {
1190    Normal {
1191        type_: TokenStream,
1192    },
1193    Into {
1194        type_: TokenStream,
1195    },
1196    Custom {
1197        type_: TokenStream,
1198        convert: TokenStream,
1199    },
1200}