1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//!
//! Utilities for manipulating identifiers, including keyword handling.
//!
/// Define a private namespace for all its items.
mod private
{
use crate :: *; // Use crate's prelude/exposed items
use convert_case ::Casing;
use proc_macro2 ::Ident;
// use syn ::spanned ::Spanned; // Needed for span
/// Ensures keyword safety by applying raw identifier escaping when needed to prevent compilation errors.
///
/// Preserves the span of the original identifier.
/// Requires the `kw` feature.
///
/// # Example
/// ```rust
/// use macro_tools :: { syn, format_ident, ident };
///
/// let ident_normal = format_ident!( "my_var" );
/// let ident_keyword = format_ident!( "fn" );
///
/// let got_normal = ident ::ident_maybe_raw( &ident_normal );
/// let got_keyword = ident ::ident_maybe_raw( &ident_keyword );
///
/// assert_eq!( got_normal.to_string(), "my_var" );
/// assert_eq!( got_keyword.to_string(), "r#fn" );
/// ```
#[ must_use ]
pub fn ident_maybe_raw(ident: &syn ::Ident) -> Ident
{
let name = ident.to_string();
if kw ::is(&name)
{
// Use r# prefix if the name is a keyword
format_ident!("r#{}", name, span = ident.span())
} else {
// Otherwise, use the name directly (cloned)
ident.clone()
}
}
/// Transforms identifier casing while preserving keyword safety to support code generation scenarios
/// that require consistent naming conventions.
///
/// # Arguments
///
/// * `original` - The original `syn ::Ident` to convert.
/// * `case` - The target `convert_case ::Case` to convert the identifier to.
///
/// # Returns
///
/// Maintains span information and raw identifier semantics to ensure generated code correctness.
///
/// # Examples
///
/// ```rust
/// use macro_tools :: { syn, format_ident };
/// use convert_case ::Case;
///
/// let ident_normal = format_ident!( "my_variable" );
/// let ident_keyword = format_ident!( "r#fn" );
///
/// // Convert to PascalCase
/// let got_pascal = macro_tools ::ident ::cased_ident_from_ident( &ident_normal, Case ::Pascal );
/// assert_eq!( got_pascal.to_string(), "MyVariable" );
///
/// // Convert a raw identifier to SnakeCase
/// let got_snake_raw = macro_tools ::ident ::cased_ident_from_ident( &ident_keyword, Case ::Snake );
/// assert_eq!( got_snake_raw.to_string(), "r#fn" );
///
/// // Convert a raw identifier: the raw prefix is preserved in the output.
/// let ident_raw_struct = format_ident!( "r#struct" );
/// let got_pascal_raw = macro_tools ::ident ::cased_ident_from_ident( &ident_raw_struct, Case ::Pascal );
/// assert_eq!( got_pascal_raw.to_string(), "r#Struct" ); // Raw prefix is always preserved.
/// ```
#[ must_use ]
pub fn cased_ident_from_ident(original: &syn ::Ident, case: convert_case ::Case) -> syn ::Ident
{
let original_str = original.to_string();
let had_raw_prefix = original_str.starts_with("r#");
let core_str = if had_raw_prefix { &original_str[2..] } else { &original_str };
let cased_str = core_str.to_case(case);
// If the cased form is not a valid identifier (e.g. kebab produces "my-var"),
// fall back to the original identifier unchanged.
let is_valid_ident =
{
let mut chars = cased_str.chars();
match chars.next()
{
Some( c ) if c == '_' || c.is_alphabetic() => chars.all( | c | c == '_' || c.is_alphanumeric() ),
_ => false,
}
};
if !is_valid_ident
{
return original.clone();
}
if kw ::is( core_str ) || kw ::is( &cased_str )
{
syn ::Ident ::new_raw( &cased_str, original.span() )
}
else
{
syn ::Ident ::new( &cased_str, original.span() )
}
}
/// Creates an identifier from a string, validating identifier syntax and handling keyword escaping.
///
/// If `raw` is `true` or the string is a Rust keyword, produces a raw identifier (`r#name`).
///
/// # Errors
///
/// Returns `Err` if the string is empty or contains characters invalid for a Rust identifier.
pub fn new_ident_from_cased_str
(
s : &str,
span : proc_macro2 ::Span,
raw : bool,
) -> crate ::Result< syn ::Ident >
{
if s.is_empty()
{
return Err( syn ::Error ::new( span, "identifier string must not be empty" ) );
}
let valid =
{
let mut chars = s.chars();
match chars.next()
{
Some( c ) if c == '_' || c.is_alphabetic() => chars.all( | c | c == '_' || c.is_alphanumeric() ),
_ => false,
}
};
if !valid
{
return Err( syn ::Error ::new( span, format!( "invalid identifier: {s:?}" ) ) );
}
if raw || kw ::is( s )
{
Ok( syn ::Ident ::new_raw( s, span ) )
}
else
{
Ok( syn ::Ident ::new( s, span ) )
}
}
}
#[ doc( inline ) ]
#[ allow( unused_imports ) ]
pub use own :: *;
/// Own namespace of the module.
#[ allow( unused_imports ) ]
pub mod own
{
use super :: *;
#[ doc( inline ) ]
pub use orphan :: *;
#[ doc( inline ) ]
pub use private ::ident_maybe_raw;
#[ doc( inline ) ]
pub use private ::cased_ident_from_ident;
#[ doc( inline ) ]
pub use private ::new_ident_from_cased_str;
}
/// Orphan namespace of the module.
#[ allow( unused_imports ) ]
pub mod orphan
{
use super :: *;
#[ doc( inline ) ]
pub use exposed :: *;
}
/// Exposed namespace of the module.
#[ allow( unused_imports ) ]
pub mod exposed
{
use super :: *;
pub use super ::super ::ident; // Use the new module name
#[ doc( inline ) ]
pub use prelude :: *;
}
/// Prelude to use essentials: `use my_module ::prelude :: *`.
#[ allow( unused_imports ) ]
pub mod prelude
{
use super :: *;
}