Skip to main content

reifydb_macro_impl/
from_frame.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use proc_macro2::{TokenStream, TokenTree};
5
6use crate::{
7	generate::{
8		arrow, braces, brackets, fat_arrow, ident, literal_str, literal_usize, parens, path, path_sep, punct,
9		punct_joint, underscore,
10	},
11	parse::{ParsedField, ParsedStruct},
12};
13
14/// Expand a parsed struct into the FromFrame implementation.
15pub fn expand(parsed: ParsedStruct) -> TokenStream {
16	let struct_name = parsed.name.to_string();
17	let struct_name_lit = literal_str(&struct_name);
18	let crate_path = &parsed.crate_path;
19
20	let mut tokens = Vec::new();
21
22	// impl ::crate_path::value::frame::from_frame::FromFrame for StructName
23	tokens.push(ident("impl"));
24	tokens.extend(path(&["", crate_path, "value", "frame", "from_frame", "FromFrame"]));
25	tokens.push(ident("for"));
26	tokens.push(TokenTree::Ident(parsed.name.clone()));
27
28	// Implementation body
29	let impl_body = generate_from_frame_impl(&parsed.fields, &struct_name_lit, crate_path);
30	tokens.push(braces(impl_body));
31
32	// Note: TryFrom<&Frame> impl is NOT generated because it would violate orphan rules
33	// when the derive is used outside reifydb-type. Use FromFrame::from_frame() instead.
34
35	tokens.into_iter().collect()
36}
37
38/// Generate the FromFrame::from_frame method body.
39fn generate_from_frame_impl(fields: &[ParsedField], struct_name_lit: &TokenTree, crate_path: &str) -> Vec<TokenTree> {
40	let mut tokens = Vec::new();
41
42	// fn from_frame(frame: &::crate_path::value::frame::frame::Frame) -> Result<Vec<Self>,
43	// ::crate_path::value::frame::from_frame::FromFrameError>
44	tokens.push(ident("fn"));
45	tokens.push(ident("from_frame"));
46	tokens.push(parens([ident("frame"), punct(':'), punct('&')]
47		.into_iter()
48		.chain(path(&["", crate_path, "value", "frame", "frame", "Frame"]))));
49	tokens.extend(arrow());
50	tokens.push(ident("Result"));
51	tokens.push(punct('<'));
52	tokens.push(ident("Vec"));
53	tokens.push(punct('<'));
54	tokens.push(ident("Self"));
55	tokens.push(punct('>'));
56	tokens.push(punct(','));
57	tokens.extend(path(&["", crate_path, "value", "frame", "from_frame", "FromFrameError"]));
58	tokens.push(punct('>'));
59
60	// Method body
61	let mut body = Vec::new();
62
63	// Column lookups
64	for field in fields {
65		if field.attrs.skip {
66			continue;
67		}
68		body.extend(generate_column_lookup(field, struct_name_lit, crate_path));
69	}
70
71	// Row count
72	body.push(ident("let"));
73	body.push(ident("row_count"));
74	body.push(punct('='));
75	body.push(ident("frame"));
76	body.push(punct('.'));
77	body.push(ident("columns"));
78	body.push(punct('.'));
79	body.push(ident("first"));
80	body.push(parens([]));
81	body.push(punct('.'));
82	body.push(ident("map"));
83	body.push(parens([
84		punct('|'),
85		ident("c"),
86		punct('|'),
87		ident("c"),
88		punct('.'),
89		ident("data"),
90		punct('.'),
91		ident("len"),
92		parens([]),
93	]));
94	body.push(punct('.'));
95	body.push(ident("unwrap_or"));
96	body.push(parens([literal_usize(0)]));
97	body.push(punct(';'));
98
99	// Field extractions
100	for field in fields {
101		if field.attrs.skip {
102			continue;
103		}
104		body.extend(generate_field_extraction(field, crate_path));
105	}
106
107	// Result construction
108	body.push(ident("let"));
109	body.push(ident("mut"));
110	body.push(ident("result"));
111	body.push(punct('='));
112	body.push(ident("Vec"));
113	body.extend(path_sep());
114	body.push(ident("with_capacity"));
115	body.push(parens([ident("row_count")]));
116	body.push(punct(';'));
117
118	// for loop
119	body.push(ident("for"));
120	body.push(ident("i"));
121	body.push(ident("in"));
122	body.push(literal_usize(0));
123	body.extend([punct_joint('.'), punct('.')]);
124	body.push(ident("row_count"));
125
126	// Loop body
127	let mut loop_body = Vec::new();
128	loop_body.push(ident("result"));
129	loop_body.push(punct('.'));
130	loop_body.push(ident("push"));
131
132	// Self { field: values_field[i].clone(), ... }
133	let mut constructor = vec![ident("Self")];
134	let mut field_inits = Vec::new();
135	for (i, field) in fields.iter().enumerate() {
136		if i > 0 {
137			field_inits.push(punct(','));
138		}
139		field_inits.push(TokenTree::Ident(field.name.clone()));
140		field_inits.push(punct(':'));
141		if field.attrs.skip {
142			field_inits.extend(path(&["", "std", "default", "Default", "default"]));
143			field_inits.push(parens([]));
144		} else {
145			let values_var = format!("values_{}", field.safe_name());
146			field_inits.push(ident(&values_var));
147			field_inits.push(brackets([ident("i")]));
148			field_inits.push(punct('.'));
149			field_inits.push(ident("clone"));
150			field_inits.push(parens([]));
151		}
152	}
153	constructor.push(braces(field_inits));
154	loop_body.push(parens(constructor));
155	loop_body.push(punct(';'));
156
157	body.push(braces(loop_body));
158
159	// Ok(result)
160	body.push(ident("Ok"));
161	body.push(parens([ident("result")]));
162
163	tokens.push(braces(body));
164	tokens
165}
166
167/// Generate column lookup for a field.
168fn generate_column_lookup(field: &ParsedField, struct_name_lit: &TokenTree, crate_path: &str) -> Vec<TokenTree> {
169	let mut tokens = Vec::new();
170	let column_name = field.column_name();
171	let col_var = format!("col_{}", field.safe_name());
172
173	tokens.push(ident("let"));
174	tokens.push(ident(&col_var));
175	tokens.push(punct(':'));
176
177	if field.attrs.optional {
178		// Option<&::crate_path::value::frame::column::FrameColumn>
179		tokens.push(ident("Option"));
180		tokens.push(punct('<'));
181		tokens.push(punct('&'));
182		tokens.extend(path(&["", crate_path, "value", "frame", "column", "FrameColumn"]));
183		tokens.push(punct('>'));
184		tokens.push(punct('='));
185		tokens.push(ident("frame"));
186		tokens.push(punct('.'));
187		tokens.push(ident("columns"));
188		tokens.push(punct('.'));
189		tokens.push(ident("iter"));
190		tokens.push(parens([]));
191		tokens.push(punct('.'));
192		tokens.push(ident("find"));
193		tokens.push(parens([
194			punct('|'),
195			ident("c"),
196			punct('|'),
197			ident("c"),
198			punct('.'),
199			ident("name"),
200			punct_joint('='),
201			punct('='),
202			literal_str(&column_name),
203		]));
204	} else {
205		// &::crate_path::value::frame::column::FrameColumn
206		tokens.push(punct('&'));
207		tokens.extend(path(&["", crate_path, "value", "frame", "column", "FrameColumn"]));
208		tokens.push(punct('='));
209		tokens.push(ident("frame"));
210		tokens.push(punct('.'));
211		tokens.push(ident("columns"));
212		tokens.push(punct('.'));
213		tokens.push(ident("iter"));
214		tokens.push(parens([]));
215		tokens.push(punct('.'));
216		tokens.push(ident("find"));
217		tokens.push(parens([
218			punct('|'),
219			ident("c"),
220			punct('|'),
221			ident("c"),
222			punct('.'),
223			ident("name"),
224			punct_joint('='),
225			punct('='),
226			literal_str(&column_name),
227		]));
228		tokens.push(punct('.'));
229		tokens.push(ident("ok_or_else"));
230
231		// || ::crate_path::value::frame::from_frame::FromFrameError::MissingColumn { ... }
232		let mut error_closure = Vec::new();
233		error_closure.extend([punct('|'), punct('|')]);
234		error_closure.extend(path(&[
235			"",
236			crate_path,
237			"value",
238			"frame",
239			"from_frame",
240			"FromFrameError",
241			"MissingColumn",
242		]));
243		error_closure.push(braces([
244			ident("column"),
245			punct(':'),
246			literal_str(&column_name),
247			punct('.'),
248			ident("to_string"),
249			parens([]),
250			punct(','),
251			ident("struct_name"),
252			punct(':'),
253			struct_name_lit.clone(),
254			punct(','),
255		]));
256		tokens.push(parens(error_closure));
257		tokens.push(punct('?'));
258	}
259
260	tokens.push(punct(';'));
261	tokens
262}
263
264/// Generate field extraction for a field.
265fn generate_field_extraction(field: &ParsedField, crate_path: &str) -> Vec<TokenTree> {
266	let mut tokens = Vec::new();
267	let column_name = field.column_name();
268	let col_var = format!("col_{}", field.safe_name());
269	let values_var = format!("values_{}", field.safe_name());
270
271	tokens.push(ident("let"));
272	tokens.push(ident(&values_var));
273	tokens.push(punct(':'));
274
275	// Type: Vec<FieldType>
276	tokens.push(ident("Vec"));
277	tokens.push(punct('<'));
278	tokens.extend(field.ty.iter().cloned());
279	tokens.push(punct('>'));
280
281	let trait_name = if field.attrs.coerce {
282		"TryFromValueCoerce"
283	} else {
284		"TryFromValue"
285	};
286
287	let method_name = if field.attrs.coerce {
288		"try_from_value_coerce"
289	} else {
290		"try_from_value"
291	};
292
293	if field.attrs.optional {
294		// let values_x = match col_x { Some(col) => ..., None => vec![None; row_count] };
295		tokens.push(punct('='));
296
297		// match col_var { Some(col) => ..., None => vec![None; row_count] }
298		tokens.push(ident("match"));
299		tokens.push(ident(&col_var));
300
301		let mut match_body = Vec::new();
302
303		// Some(col) => { ... }
304		match_body.push(ident("Some"));
305		match_body.push(parens([ident("col")]));
306		match_body.extend(fat_arrow());
307
308		let mut some_body = vec![
309			ident("col"),
310			punct('.'),
311			ident("data"),
312			punct('.'),
313			ident("iter"),
314			parens([]),
315			punct('.'),
316			ident("enumerate"),
317			parens([]),
318			punct('.'),
319			ident("map"),
320		];
321		some_body.push(parens(generate_optional_map_closure(
322			trait_name,
323			method_name,
324			&column_name,
325			crate_path,
326		)));
327		some_body.push(punct('.'));
328		some_body.push(ident("collect"));
329		// ::<Result<_, ::crate_path::FromFrameError>>
330		some_body.extend(path_sep());
331		some_body.push(punct('<'));
332		some_body.push(ident("Result"));
333		some_body.push(punct('<'));
334		some_body.push(underscore());
335		some_body.push(punct(','));
336		some_body.extend(path(&["", crate_path, "value", "frame", "from_frame", "FromFrameError"]));
337		some_body.push(punct('>'));
338		some_body.push(punct('>'));
339		some_body.push(parens([]));
340		some_body.push(punct('?'));
341
342		match_body.push(braces(some_body));
343		match_body.push(punct(','));
344
345		// None => vec![None; row_count]
346		match_body.push(ident("None"));
347		match_body.extend(fat_arrow());
348		match_body.push(ident("vec"));
349		match_body.push(punct('!'));
350		match_body.push(brackets([ident("None"), punct(';'), ident("row_count")]));
351		match_body.push(punct(','));
352
353		tokens.push(braces(match_body));
354	} else {
355		// let values_x = col_x.data.iter()...collect::<Result<_, FromFrameError>>()?;
356		tokens.push(punct('='));
357
358		tokens.push(ident(&col_var));
359		tokens.push(punct('.'));
360		tokens.push(ident("data"));
361		tokens.push(punct('.'));
362		tokens.push(ident("iter"));
363		tokens.push(parens([]));
364		tokens.push(punct('.'));
365		tokens.push(ident("enumerate"));
366		tokens.push(parens([]));
367		tokens.push(punct('.'));
368		tokens.push(ident("map"));
369		tokens.push(parens(generate_required_map_closure(
370			trait_name,
371			method_name,
372			&column_name,
373			&field.ty,
374			crate_path,
375		)));
376		tokens.push(punct('.'));
377		tokens.push(ident("collect"));
378		// ::<Result<_, ::crate_path::FromFrameError>>
379		tokens.extend(path_sep());
380		tokens.push(punct('<'));
381		tokens.push(ident("Result"));
382		tokens.push(punct('<'));
383		tokens.push(underscore());
384		tokens.push(punct(','));
385		tokens.extend(path(&["", crate_path, "value", "frame", "from_frame", "FromFrameError"]));
386		tokens.push(punct('>'));
387		tokens.push(punct('>'));
388		tokens.push(parens([]));
389		tokens.push(punct('?'));
390	}
391
392	tokens.push(punct(';'));
393	tokens
394}
395
396/// Generate the map closure for optional fields.
397fn generate_optional_map_closure(
398	trait_name: &str,
399	method_name: &str,
400	column_name: &str,
401	crate_path: &str,
402) -> Vec<TokenTree> {
403	let mut tokens = Vec::new();
404
405	// |(row, v)| { if matches!(v, ...) { Ok(None) } else { ... } }
406	tokens.push(punct('|'));
407	tokens.push(parens([ident("row"), punct(','), ident("v")]));
408	tokens.push(punct('|'));
409
410	// if matches!(v, ::crate_path::Value::None { .. })
411	let mut body = vec![
412		ident("if"),
413		ident("matches"),
414		punct('!'),
415		parens([ident("v"), punct(',')]
416			.into_iter()
417			.chain(path(&["", crate_path, "value", "Value", "None"]))
418			.chain([braces([punct_joint('.'), punct('.')])])),
419	];
420
421	// { Ok(None) }
422	body.push(braces([ident("Ok"), parens([ident("None")])]));
423
424	// else { <_ as Trait>::method(&v).map(Some).map_err(...) }
425	body.push(ident("else"));
426
427	let mut else_body = Vec::new();
428	else_body.push(punct('<'));
429	else_body.push(underscore());
430	else_body.push(ident("as"));
431	else_body.extend(path(&["", crate_path, "value", "try_from", trait_name]));
432	else_body.push(punct('>'));
433	else_body.extend(path_sep());
434	else_body.push(ident(method_name));
435	else_body.push(parens([punct('&'), ident("v")]));
436	else_body.push(punct('.'));
437	else_body.push(ident("map"));
438	else_body.push(parens([ident("Some")]));
439	else_body.push(punct('.'));
440	else_body.push(ident("map_err"));
441	else_body.push(parens(generate_error_closure(column_name, crate_path)));
442
443	body.push(braces(else_body));
444
445	tokens.push(braces(body));
446	tokens
447}
448
449/// Generate the map closure for required fields.
450fn generate_required_map_closure(
451	trait_name: &str,
452	method_name: &str,
453	column_name: &str,
454	field_ty: &[TokenTree],
455	crate_path: &str,
456) -> Vec<TokenTree> {
457	let mut tokens = Vec::new();
458
459	// |(row, v)| { <Type as Trait>::method(&v).map_err(...) }
460	tokens.push(punct('|'));
461	tokens.push(parens([ident("row"), punct(','), ident("v")]));
462	tokens.push(punct('|'));
463
464	let mut body = Vec::new();
465	body.push(punct('<'));
466	body.extend(field_ty.iter().cloned());
467	body.push(ident("as"));
468	body.extend(path(&["", crate_path, "value", "try_from", trait_name]));
469	body.push(punct('>'));
470	body.extend(path_sep());
471	body.push(ident(method_name));
472	body.push(parens([punct('&'), ident("v")]));
473	body.push(punct('.'));
474	body.push(ident("map_err"));
475	body.push(parens(generate_error_closure(column_name, crate_path)));
476
477	tokens.push(braces(body));
478	tokens
479}
480
481/// Generate the error closure for map_err.
482fn generate_error_closure(column_name: &str, crate_path: &str) -> Vec<TokenTree> {
483	let mut tokens = Vec::new();
484
485	// |e| ::crate_path::FromFrameError::ValueError { column: "...", row, error: e }
486	tokens.push(punct('|'));
487	tokens.push(ident("e"));
488	tokens.push(punct('|'));
489	tokens.extend(path(&["", crate_path, "value", "frame", "from_frame", "FromFrameError", "ValueError"]));
490	tokens.push(braces([
491		ident("column"),
492		punct(':'),
493		literal_str(column_name),
494		punct('.'),
495		ident("to_string"),
496		parens([]),
497		punct(','),
498		ident("row"),
499		punct(','),
500		ident("error"),
501		punct(':'),
502		ident("e"),
503		punct(','),
504	]));
505
506	tokens
507}