1#![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)); 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 .map(|s| format!(" {s}"))
845 .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 "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 "abstract" | "async" | "become" | "box" | "do" | "final" | "macro" | "override"
978 | "priv" | "typeof" | "unsized" | "virtual" | "yield" => true,
979 "union" | "dyn" => true,
981 "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 let mut components = vec![quote!(super)];
1042
1043 for _ in 0..this_module_path.len() - shared_prefix {
1045 components.push(quote!(super));
1046 }
1047
1048 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 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 *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 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 .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 (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}