Skip to main content

reifydb_macro_impl/
generate.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
5
6/// Creates an identifier token.
7pub fn ident(name: &str) -> TokenTree {
8	TokenTree::Ident(Ident::new(name, Span::call_site()))
9}
10
11/// Creates an underscore token for type inference placeholders.
12/// This is needed because Ident::new("_") panics in proc_macro v1.
13pub fn underscore() -> TokenTree {
14	"_".parse::<TokenStream>().unwrap().into_iter().next().unwrap()
15}
16
17/// Creates an identifier token, handling raw identifiers (r#keyword).
18#[allow(dead_code)]
19pub fn ident_raw(name: &str) -> TokenTree {
20	if let Some(stripped) = name.strip_prefix("r#") {
21		TokenTree::Ident(Ident::new_raw(stripped, Span::call_site()))
22	} else {
23		TokenTree::Ident(Ident::new(name, Span::call_site()))
24	}
25}
26
27/// Creates a punctuation token with Alone spacing.
28pub fn punct(ch: char) -> TokenTree {
29	TokenTree::Punct(Punct::new(ch, Spacing::Alone))
30}
31
32/// Creates a punctuation token with Joint spacing (for multi-char punctuation like ::).
33pub fn punct_joint(ch: char) -> TokenTree {
34	TokenTree::Punct(Punct::new(ch, Spacing::Joint))
35}
36
37/// Creates a string literal token.
38pub fn literal_str(s: &str) -> TokenTree {
39	TokenTree::Literal(Literal::string(s))
40}
41
42/// Creates an integer literal token.
43pub fn literal_usize(n: usize) -> TokenTree {
44	TokenTree::Literal(Literal::usize_unsuffixed(n))
45}
46
47/// Creates a group (delimited tokens).
48pub fn group(delimiter: Delimiter, tokens: impl IntoIterator<Item = TokenTree>) -> TokenTree {
49	TokenTree::Group(Group::new(delimiter, tokens.into_iter().collect()))
50}
51
52/// Creates parentheses group: (tokens)
53pub fn parens(tokens: impl IntoIterator<Item = TokenTree>) -> TokenTree {
54	group(Delimiter::Parenthesis, tokens)
55}
56
57/// Creates brace group: {tokens}
58pub fn braces(tokens: impl IntoIterator<Item = TokenTree>) -> TokenTree {
59	group(Delimiter::Brace, tokens)
60}
61
62/// Creates bracket group: [tokens]
63pub fn brackets(tokens: impl IntoIterator<Item = TokenTree>) -> TokenTree {
64	group(Delimiter::Bracket, tokens)
65}
66
67/// Emits `::` (path separator).
68pub fn path_sep() -> impl Iterator<Item = TokenTree> {
69	[punct_joint(':'), punct(':')].into_iter()
70}
71
72/// Emits a path like `::reifydb_type::FromFrame`.
73/// Pass an empty first segment for a global path (::foo::bar).
74pub fn path(segments: &[&str]) -> Vec<TokenTree> {
75	let mut tokens = Vec::new();
76	let mut prev_was_empty = false;
77
78	for (i, seg) in segments.iter().enumerate() {
79		// Determine if we need to emit ::
80		// - First segment: emit :: only if segment is empty (global path marker)
81		// - Later segments: emit :: only if previous segment was NOT empty
82		let need_sep = if i == 0 {
83			seg.is_empty() // Leading :: for global paths
84		} else {
85			!prev_was_empty // Add :: only if prev was not the empty global marker
86		};
87
88		if need_sep {
89			tokens.extend(path_sep());
90		}
91
92		if !seg.is_empty() {
93			tokens.push(ident(seg));
94		}
95
96		prev_was_empty = seg.is_empty();
97	}
98	tokens
99}
100
101/// Emits `->` (return type arrow).
102pub fn arrow() -> impl Iterator<Item = TokenTree> {
103	[punct_joint('-'), punct('>')].into_iter()
104}
105
106/// Emits `=>` (match arm arrow).
107pub fn fat_arrow() -> impl Iterator<Item = TokenTree> {
108	[punct_joint('='), punct('>')].into_iter()
109}
110
111/// Emits `<` and `>` around tokens for generics.
112#[allow(dead_code)]
113pub fn generics(inner: impl IntoIterator<Item = TokenTree>) -> Vec<TokenTree> {
114	let mut tokens = vec![punct('<')];
115	tokens.extend(inner);
116	tokens.push(punct('>'));
117	tokens
118}
119
120/// Convenience: extends a Vec with an iterator.
121#[allow(dead_code)]
122pub fn extend(tokens: &mut Vec<TokenTree>, iter: impl IntoIterator<Item = TokenTree>) {
123	tokens.extend(iter);
124}
125
126/// Creates a compile_error!("message") invocation.
127pub fn compile_error(message: &str) -> TokenStream {
128	let tokens = vec![ident("compile_error"), punct('!'), parens([literal_str(message)])];
129	tokens.into_iter().collect()
130}