Skip to content

Commit 8ae1368

Browse files
committed
Optimize
1 parent 37c63b2 commit 8ae1368

3 files changed

Lines changed: 76 additions & 87 deletions

File tree

crates/vespera_macro/src/openapi_generator.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,8 @@ fn build_schema_lookups(
107107
let mut struct_definitions = HashMap::with_capacity(metadata.structs.len());
108108

109109
for struct_meta in &metadata.structs {
110-
let name = struct_meta.name.clone();
111-
struct_definitions.insert(name.clone(), struct_meta.definition.clone());
112-
known_schema_names.insert(name);
110+
struct_definitions.insert(struct_meta.name.clone(), struct_meta.definition.clone());
111+
known_schema_names.insert(struct_meta.name.clone());
113112
}
114113

115114
(known_schema_names, struct_definitions)

crates/vespera_macro/src/schema_macro/codegen.rs

Lines changed: 61 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ pub fn generate_filtered_schema(
109109
schema_type: Some(vespera::schema::SchemaType::Object),
110110
properties: if properties.is_empty() { None } else { Some(properties) },
111111
required: #required_tokens,
112-
..vespera::schema::Schema::new(vespera::schema::SchemaType::Object)
112+
..vespera::schema::Schema::default()
113113
}
114114
}
115115
}
@@ -133,95 +133,87 @@ pub fn schema_ref_to_tokens(schema_ref: &SchemaRef) -> TokenStream {
133133
}
134134
}
135135

136-
/// Convert Schema to `TokenStream` for code generation
137-
#[allow(clippy::option_if_let_else)]
136+
/// Convert Schema to `TokenStream` for code generation.
137+
///
138+
/// Only emits non-None fields, using `..Default::default()` for the rest.
139+
/// This reduces generated code volume by ~70% for typical schemas
140+
/// (e.g., a String field: 3 tokens instead of 10).
138141
pub fn schema_to_tokens(schema: &Schema) -> TokenStream {
139-
let schema_type_tokens = match &schema.schema_type {
140-
Some(SchemaType::String) => quote! { Some(vespera::schema::SchemaType::String) },
141-
Some(SchemaType::Number) => quote! { Some(vespera::schema::SchemaType::Number) },
142-
Some(SchemaType::Integer) => quote! { Some(vespera::schema::SchemaType::Integer) },
143-
Some(SchemaType::Boolean) => quote! { Some(vespera::schema::SchemaType::Boolean) },
144-
Some(SchemaType::Array) => quote! { Some(vespera::schema::SchemaType::Array) },
145-
Some(SchemaType::Object) => quote! { Some(vespera::schema::SchemaType::Object) },
146-
Some(SchemaType::Null) => quote! { Some(vespera::schema::SchemaType::Null) },
147-
None => quote! { None },
148-
};
142+
let mut fields: Vec<TokenStream> = Vec::new();
143+
144+
// schema_type
145+
if let Some(st) = &schema.schema_type {
146+
let st_tokens = match st {
147+
SchemaType::String => quote! { vespera::schema::SchemaType::String },
148+
SchemaType::Number => quote! { vespera::schema::SchemaType::Number },
149+
SchemaType::Integer => quote! { vespera::schema::SchemaType::Integer },
150+
SchemaType::Boolean => quote! { vespera::schema::SchemaType::Boolean },
151+
SchemaType::Array => quote! { vespera::schema::SchemaType::Array },
152+
SchemaType::Object => quote! { vespera::schema::SchemaType::Object },
153+
SchemaType::Null => quote! { vespera::schema::SchemaType::Null },
154+
};
155+
fields.push(quote! { schema_type: Some(#st_tokens) });
156+
}
149157

150-
let format_tokens = if let Some(f) = &schema.format {
151-
quote! { Some(#f.to_string()) }
152-
} else {
153-
quote! { None }
154-
};
158+
// ref_path
159+
if let Some(rp) = &schema.ref_path {
160+
fields.push(quote! { ref_path: Some(#rp.to_string()) });
161+
}
155162

156-
let nullable_tokens = match schema.nullable {
157-
Some(true) => quote! { Some(true) },
158-
Some(false) => quote! { Some(false) },
159-
None => quote! { None },
160-
};
163+
// format
164+
if let Some(f) = &schema.format {
165+
fields.push(quote! { format: Some(#f.to_string()) });
166+
}
161167

162-
let ref_path_tokens = if let Some(rp) = &schema.ref_path {
163-
quote! { Some(#rp.to_string()) }
164-
} else {
165-
quote! { None }
166-
};
168+
// nullable
169+
if let Some(n) = schema.nullable {
170+
fields.push(quote! { nullable: Some(#n) });
171+
}
167172

168-
let items_tokens = if let Some(items) = &schema.items {
173+
// items
174+
if let Some(items) = &schema.items {
169175
let inner = schema_ref_to_tokens(items);
170-
quote! { Some(Box::new(#inner)) }
171-
} else {
172-
quote! { None }
173-
};
176+
fields.push(quote! { items: Some(Box::new(#inner)) });
177+
}
174178

175-
let properties_tokens = if let Some(props) = &schema.properties {
179+
// properties
180+
if let Some(props) = &schema.properties {
176181
let entries: Vec<_> = props
177182
.iter()
178183
.map(|(k, v)| {
179184
let v_tokens = schema_ref_to_tokens(v);
180185
quote! { (#k.to_string(), #v_tokens) }
181186
})
182187
.collect();
183-
quote! {
184-
Some({
188+
fields.push(quote! {
189+
properties: Some({
185190
let mut map = std::collections::BTreeMap::new();
186191
#(map.insert(#entries.0, #entries.1);)*
187192
map
188193
})
189-
}
190-
} else {
191-
quote! { None }
192-
};
194+
});
195+
}
193196

194-
let required_tokens = if let Some(req) = &schema.required {
197+
// required
198+
if let Some(req) = &schema.required {
195199
let req_strs: Vec<_> = req.iter().map(std::string::String::as_str).collect();
196-
quote! { Some(vec![#(#req_strs.to_string()),*]) }
197-
} else {
198-
quote! { None }
199-
};
200+
fields.push(quote! { required: Some(vec![#(#req_strs.to_string()),*]) });
201+
}
200202

201-
let minimum_tokens = if let Some(min) = schema.minimum {
202-
quote! { Some(#min) }
203-
} else {
204-
quote! { None }
205-
};
203+
// minimum
204+
if let Some(min) = schema.minimum {
205+
fields.push(quote! { minimum: Some(#min) });
206+
}
206207

207-
let maximum_tokens = if let Some(max) = schema.maximum {
208-
quote! { Some(#max) }
209-
} else {
210-
quote! { None }
211-
};
208+
// maximum
209+
if let Some(max) = schema.maximum {
210+
fields.push(quote! { maximum: Some(#max) });
211+
}
212212

213213
quote! {
214214
vespera::schema::Schema {
215-
ref_path: #ref_path_tokens,
216-
schema_type: #schema_type_tokens,
217-
format: #format_tokens,
218-
nullable: #nullable_tokens,
219-
items: #items_tokens,
220-
properties: #properties_tokens,
221-
required: #required_tokens,
222-
minimum: #minimum_tokens,
223-
maximum: #maximum_tokens,
224-
..vespera::schema::Schema::new(vespera::schema::SchemaType::Object)
215+
#(#fields,)*
216+
..vespera::schema::Schema::default()
225217
}
226218
}
227219
}
@@ -370,7 +362,10 @@ mod tests {
370362
};
371363
let tokens = schema_to_tokens(&schema);
372364
let output = tokens.to_string();
373-
assert!(output.contains("schema_type : None"));
365+
// With conditional emission, schema_type is omitted when None
366+
// (..Default::default() provides None)
367+
assert!(!output.contains("schema_type"));
368+
assert!(output.contains("default"));
374369
}
375370

376371
#[test]

crates/vespera_macro/src/schema_macro/from_model.rs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,17 @@ pub fn build_entity_path_from_schema_path(
2424
schema_path: &TokenStream,
2525
_source_module_path: &[String],
2626
) -> TokenStream {
27-
// Parse the schema path to extract segments
27+
// Parse the schema path, replace "Schema" with "Entity", and build Idents in one pass
2828
let path_str = schema_path.to_string();
29-
let segments: Vec<&str> = path_str.split("::").map(str::trim).collect();
30-
31-
// Replace "Schema" with "Entity" in the last segment
32-
let entity_segments: Vec<String> = segments
33-
.iter()
29+
let path_idents: Vec<syn::Ident> = path_str
30+
.split("::")
3431
.map(|s| {
35-
if *s == "Schema" {
36-
"Entity".to_string()
37-
} else {
38-
s.to_string()
39-
}
32+
let s = s.trim();
33+
let name = if s == "Schema" { "Entity" } else { s };
34+
syn::Ident::new(name, proc_macro2::Span::call_site())
4035
})
4136
.collect();
4237

43-
// Build the path tokens
44-
let path_idents: Vec<syn::Ident> = entity_segments
45-
.iter()
46-
.map(|s| syn::Ident::new(s, proc_macro2::Span::call_site()))
47-
.collect();
48-
4938
quote! { #(#path_idents)::* }
5039
}
5140

@@ -241,14 +230,20 @@ pub fn generate_from_model_with_relations(
241230
vec![]
242231
};
243232

233+
// Pre-build relation lookup for O(1) access in field assignments loop
234+
let relation_by_name: HashMap<&syn::Ident, &RelationFieldInfo> = relation_fields
235+
.iter()
236+
.map(|rel| (&rel.field_name, rel))
237+
.collect();
238+
244239
// Build field assignments
245240
// For relation fields, check for circular references and use inline construction if needed
246241
let field_assignments: Vec<TokenStream> = field_mappings
247242
.iter()
248243
.map(|(new_ident, source_ident, wrapped, is_relation)| {
249244
if *is_relation {
250245
// Find the relation info for this field
251-
if let Some(rel) = relation_fields.iter().find(|r| &r.field_name == source_ident) {
246+
if let Some(rel) = relation_by_name.get(source_ident) {
252247
let schema_path = &rel.schema_path;
253248

254249
// Try to find the related MODEL definition to check for circular refs

0 commit comments

Comments
 (0)