A better Rust ATProto crate
1use super::CodeGenerator;
2use crate::lexicon::{
3 LexArrayItem, LexObjectProperty, LexPrimitiveArrayItem, LexString, LexStringFormat,
4 LexUserType, LexXrpcParametersProperty,
5};
6
7/// Trait for lexicon types that can determine lifetime requirements
8trait HasLifetime {
9 /// Check if this type needs a lifetime parameter when generated
10 fn needs_lifetime(&self, generator: &CodeGenerator) -> bool;
11}
12
13impl HasLifetime for LexObjectProperty<'_> {
14 fn needs_lifetime(&self, generator: &CodeGenerator) -> bool {
15 match self {
16 LexObjectProperty::Boolean(_) | LexObjectProperty::Integer(_) => false,
17 LexObjectProperty::String(s) => s.needs_lifetime(generator),
18 LexObjectProperty::Bytes(_) => false, // Bytes is owned
19 LexObjectProperty::CidLink(_)
20 | LexObjectProperty::Blob(_)
21 | LexObjectProperty::Unknown(_) => true,
22 LexObjectProperty::Array(array) => array.items.needs_lifetime(generator),
23 LexObjectProperty::Object(_) => true, // Nested objects have lifetimes
24 LexObjectProperty::Ref(ref_type) => generator.ref_needs_lifetime(&ref_type.r#ref),
25 LexObjectProperty::Union(_) => true, // Unions generally have lifetimes
26 }
27 }
28}
29
30impl HasLifetime for LexArrayItem<'_> {
31 fn needs_lifetime(&self, generator: &CodeGenerator) -> bool {
32 match self {
33 LexArrayItem::Boolean(_) | LexArrayItem::Integer(_) => false,
34 LexArrayItem::String(s) => s.needs_lifetime(generator),
35 LexArrayItem::Bytes(_) => false,
36 LexArrayItem::CidLink(_) | LexArrayItem::Blob(_) | LexArrayItem::Unknown(_) => true,
37 LexArrayItem::Object(_) => true, // Nested objects have lifetimes
38 LexArrayItem::Ref(ref_type) => generator.ref_needs_lifetime(&ref_type.r#ref),
39 LexArrayItem::Union(_) => true,
40 }
41 }
42}
43
44impl HasLifetime for LexString<'_> {
45 fn needs_lifetime(&self, _generator: &CodeGenerator) -> bool {
46 match self.format {
47 Some(LexStringFormat::Datetime)
48 | Some(LexStringFormat::Language)
49 | Some(LexStringFormat::Tid) => false,
50 _ => true, // Most string types borrow
51 }
52 }
53}
54
55impl HasLifetime for LexUserType<'_> {
56 fn needs_lifetime(&self, generator: &CodeGenerator) -> bool {
57 match self {
58 LexUserType::Record(_) => true,
59 LexUserType::Object(_) => true,
60 LexUserType::Token(_) => false,
61 LexUserType::String(s) => {
62 // Check if it's a known values enum or a regular string
63 if s.known_values.is_some() {
64 // Known values enums have Other(CowStr<'a>) variant
65 true
66 } else {
67 s.needs_lifetime(generator)
68 }
69 }
70 LexUserType::Integer(_) => false,
71 LexUserType::Boolean(_) => false,
72 LexUserType::Bytes(_) => false,
73 LexUserType::CidLink(_) | LexUserType::Blob(_) | LexUserType::Unknown(_) => true,
74 LexUserType::Array(array) => array.items.needs_lifetime(generator),
75 LexUserType::XrpcQuery(_)
76 | LexUserType::XrpcProcedure(_)
77 | LexUserType::XrpcSubscription(_) => {
78 // XRPC types generate multiple structs, not a single type we can reference
79 // Shouldn't be referenced directly
80 true
81 }
82 LexUserType::Union(_) => true, // Union enums are always generated with <'a>
83 }
84 }
85}
86
87impl HasLifetime for LexXrpcParametersProperty<'_> {
88 fn needs_lifetime(&self, generator: &CodeGenerator) -> bool {
89 match self {
90 LexXrpcParametersProperty::Boolean(_) | LexXrpcParametersProperty::Integer(_) => false,
91 LexXrpcParametersProperty::String(s) => s.needs_lifetime(generator),
92 LexXrpcParametersProperty::Unknown(_) => true,
93 LexXrpcParametersProperty::Array(arr) => arr.items.needs_lifetime(generator),
94 }
95 }
96}
97
98impl HasLifetime for LexPrimitiveArrayItem<'_> {
99 fn needs_lifetime(&self, generator: &CodeGenerator) -> bool {
100 match self {
101 LexPrimitiveArrayItem::Boolean(_) | LexPrimitiveArrayItem::Integer(_) => false,
102 LexPrimitiveArrayItem::String(s) => s.needs_lifetime(generator),
103 LexPrimitiveArrayItem::Unknown(_) => true,
104 }
105 }
106}
107
108impl<'c> CodeGenerator<'c> {
109 /// Check if a property type needs a lifetime parameter
110 pub(super) fn property_needs_lifetime(&self, prop: &LexObjectProperty<'_>) -> bool {
111 prop.needs_lifetime(self)
112 }
113
114 /// Check if an array item type needs a lifetime parameter
115 pub(super) fn array_item_needs_lifetime(&self, item: &LexArrayItem<'_>) -> bool {
116 item.needs_lifetime(self)
117 }
118
119 /// Check if a string type needs a lifetime parameter
120 pub(super) fn string_needs_lifetime(&self, s: &LexString<'_>) -> bool {
121 s.needs_lifetime(self)
122 }
123
124 /// Check if a ref needs a lifetime parameter
125 pub(super) fn ref_needs_lifetime(&self, ref_str: &str) -> bool {
126 // Try to resolve the ref
127 if let Some((_doc, def)) = self.corpus.resolve_ref(ref_str) {
128 def.needs_lifetime(self)
129 } else {
130 // If we can't resolve it, assume it needs a lifetime (safe default)
131 true
132 }
133 }
134
135 /// Check if xrpc params need a lifetime parameter
136 pub(super) fn params_need_lifetime(
137 &self,
138 params: &crate::lexicon::LexXrpcParameters<'_>,
139 ) -> bool {
140 params
141 .properties
142 .values()
143 .any(|prop| prop.needs_lifetime(self))
144 }
145}