1#![allow(
2 missing_docs,
3 reason = "Intentional compatibility, platform, or test-only suppression."
4)]
5use proc_macro::TokenStream;
6use proc_macro2::TokenStream as TokenStream2;
7use quote::{format_ident, quote};
8use syn::{Data, DataEnum, DeriveInput, Fields, parse_macro_input};
9
10#[proc_macro_derive(StringNewtype)]
29pub fn derive_string_newtype(input: TokenStream) -> TokenStream {
30 let input = parse_macro_input!(input as DeriveInput);
31 impl_string_newtype(&input).unwrap_or_else(|err| err.to_compile_error().into())
32}
33
34fn impl_string_newtype(input: &DeriveInput) -> syn::Result<TokenStream> {
35 let name = &input.ident;
36
37 let field_type = match &input.data {
39 Data::Struct(data) => match &data.fields {
40 Fields::Unnamed(fields) => {
41 if fields.unnamed.len() != 1 {
42 return Err(syn::Error::new_spanned(
43 name,
44 "StringNewtype requires a tuple struct with exactly one field",
45 ));
46 }
47 let Some(field) = fields.unnamed.first() else {
48 return Err(syn::Error::new_spanned(
49 name,
50 "StringNewtype requires a tuple struct with exactly one field",
51 ));
52 };
53 &field.ty
54 }
55 _ => {
56 return Err(syn::Error::new_spanned(name, "StringNewtype can only be derived for tuple structs"));
57 }
58 },
59 _ => {
60 return Err(syn::Error::new_spanned(name, "StringNewtype can only be derived for structs"));
61 }
62 };
63
64 if !is_string_type(field_type) {
66 return Err(syn::Error::new_spanned(field_type, "StringNewtype requires the inner type to be String"));
67 }
68
69 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
70
71 let output = quote! {
72 impl #impl_generics #name #ty_generics #where_clause {
73 pub fn new(value: impl Into<String>) -> Self {
75 Self(value.into())
76 }
77
78 pub fn as_str(&self) -> &str {
80 &self.0
81 }
82
83 pub fn into_inner(self) -> String {
85 self.0
86 }
87 }
88
89 impl #impl_generics std::ops::Deref for #name #ty_generics #where_clause {
90 type Target = str;
91
92 fn deref(&self) -> &Self::Target {
93 &self.0
94 }
95 }
96
97 impl #impl_generics std::borrow::Borrow<str> for #name #ty_generics #where_clause {
98 fn borrow(&self) -> &str {
99 &self.0
100 }
101 }
102
103 impl #impl_generics AsRef<str> for #name #ty_generics #where_clause {
104 fn as_ref(&self) -> &str {
105 &self.0
106 }
107 }
108
109 impl #impl_generics std::fmt::Display for #name #ty_generics #where_clause {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 self.0.fmt(f)
112 }
113 }
114
115 impl #impl_generics From<String> for #name #ty_generics #where_clause {
116 fn from(value: String) -> Self {
117 Self(value)
118 }
119 }
120
121 impl #impl_generics From<&str> for #name #ty_generics #where_clause {
122 fn from(value: &str) -> Self {
123 Self(value.to_string())
124 }
125 }
126
127 impl #impl_generics From<#name #ty_generics> for String #where_clause {
128 fn from(value: #name #ty_generics) -> Self {
129 value.0
130 }
131 }
132 };
133
134 Ok(output.into())
135}
136
137fn is_string_type(ty: &syn::Type) -> bool {
138 if let syn::Type::Path(type_path) = ty
139 && type_path.qself.is_none()
140 && type_path.path.segments.len() == 1
141 {
142 return type_path.path.segments.first().is_some_and(|segment| segment.ident == "String");
143 }
144 false
145}
146
147#[proc_macro_derive(DebugNoInline)]
178pub fn derive_debug_no_inline(input: TokenStream) -> TokenStream {
179 let input = parse_macro_input!(input as DeriveInput);
180 match impl_debug_no_inline(&input) {
181 Ok(tokens) => tokens.into(),
182 Err(err) => err.to_compile_error().into(),
183 }
184}
185
186fn impl_debug_no_inline(input: &DeriveInput) -> syn::Result<TokenStream2> {
187 let name = &input.ident;
188
189 let mut generics = input.generics.clone();
191 for param in generics.type_params_mut() {
192 param.bounds.push(syn::parse_quote!(::core::fmt::Debug));
193 }
194 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
195
196 let body = match &input.data {
197 Data::Struct(data) => fmt_body_struct(&name.to_string(), &data.fields),
198 Data::Enum(data) => fmt_body_enum(data),
199 Data::Union(_) => {
200 return Err(syn::Error::new_spanned(name, "DebugNoInline cannot be derived for unions"));
201 }
202 };
203
204 Ok(quote! {
205 #[automatically_derived]
206 impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause {
207 #[inline(never)]
208 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
209 #body
210 }
211 }
212 })
213}
214
215fn field_name(ident: &syn::Ident) -> String {
218 let raw = ident.to_string();
219 raw.strip_prefix("r#").unwrap_or(raw.as_str()).to_string()
220}
221
222fn fmt_body_struct(type_name: &str, fields: &Fields) -> TokenStream2 {
223 match fields {
224 Fields::Unit => quote! { f.write_str(#type_name) },
225 Fields::Named(named) => {
226 let calls = named.named.iter().filter_map(|field| {
227 let ident = field.ident.as_ref()?;
228 let field_name = field_name(ident);
229 Some(quote! { __debug_builder.field(#field_name, &self.#ident); })
230 });
231 let builder = debug_builder_init("debug_struct", type_name);
232 quote! {
233 #builder
234 #(#calls)*
235 __debug_builder.finish()
236 }
237 }
238 Fields::Unnamed(unnamed) => {
239 let calls = unnamed.unnamed.iter().enumerate().map(|(index, _)| {
240 let index = syn::Index::from(index);
241 quote! { __debug_builder.field(&self.#index); }
242 });
243 let builder = debug_builder_init("debug_tuple", type_name);
244 quote! {
245 #builder
246 #(#calls)*
247 __debug_builder.finish()
248 }
249 }
250 }
251}
252
253fn fmt_body_enum(data: &DataEnum) -> TokenStream2 {
254 if data.variants.is_empty() {
257 return quote! { match *self {} };
258 }
259
260 let arms = data.variants.iter().map(|variant| {
261 let variant_ident = &variant.ident;
262 let variant_name = variant.ident.to_string();
263 match &variant.fields {
264 Fields::Unit => quote! {
265 Self::#variant_ident => f.write_str(#variant_name),
266 },
267 Fields::Named(named) => {
268 let pairs: Vec<(syn::Ident, syn::Ident)> = named
269 .named
270 .iter()
271 .enumerate()
272 .filter_map(|(index, field)| {
273 let ident = field.ident.as_ref()?.clone();
274 Some((ident, format_ident!("__self_{index}")))
275 })
276 .collect();
277 let pattern = pairs.iter().map(|(ident, binding)| quote! { #ident: #binding });
278 let calls = pairs.iter().map(|(ident, binding)| {
279 let field_name = field_name(ident);
280 quote! { __debug_builder.field(#field_name, #binding); }
281 });
282 let builder = debug_builder_init("debug_struct", &variant_name);
283 quote! {
284 Self::#variant_ident { #(#pattern),* } => {
285 #builder
286 #(#calls)*
287 __debug_builder.finish()
288 }
289 }
290 }
291 Fields::Unnamed(unnamed) => {
292 let bindings: Vec<syn::Ident> = (0..unnamed.unnamed.len())
293 .map(|index| format_ident!("__self_{index}"))
294 .collect();
295 let calls = bindings.iter().map(|binding| quote! { __debug_builder.field(#binding); });
296 let builder = debug_builder_init("debug_tuple", &variant_name);
297 quote! {
298 Self::#variant_ident( #(#bindings),* ) => {
299 #builder
300 #(#calls)*
301 __debug_builder.finish()
302 }
303 }
304 }
305 }
306 });
307 quote! {
308 match self {
309 #(#arms)*
310 }
311 }
312}
313
314fn debug_builder_init(kind: &str, name: &str) -> TokenStream2 {
317 let kind = format_ident!("{kind}");
318 quote! { let mut __debug_builder = f.#kind(#name); }
319}