This commit is contained in:
Rowan 2025-07-09 12:47:46 -04:00
parent 19a025d47f
commit 0a72d4312f

View file

@ -2,7 +2,7 @@ mod attr_args;
extern crate proc_macro; extern crate proc_macro;
use crate::attr_args::{OsDisplayAttribute}; use crate::attr_args::OsDisplayAttribute;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
use syn::parse::Parse; use syn::parse::Parse;
@ -36,6 +36,7 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
match parsed_attr { match parsed_attr {
OsDisplayAttribute::Transparent => { OsDisplayAttribute::Transparent => {
match &variant.fields { match &variant.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
quote_spanned! {variant.span() => quote_spanned! {variant.span() =>
@ -62,6 +63,8 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
} }
} }
OsDisplayAttribute::FromDisplay => { OsDisplayAttribute::FromDisplay => {
match &variant.fields { match &variant.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
quote_spanned! {variant.span() => quote_spanned! {variant.span() =>
@ -88,6 +91,7 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
} }
} }
OsDisplayAttribute::Format(format_args) => { OsDisplayAttribute::Format(format_args) => {
let format_str_value = format_args.format_string.value(); let format_str_value = format_args.format_string.value();
let positional_expressions: Vec<&Expr> = format_args.positional_args.iter().collect(); let positional_expressions: Vec<&Expr> = format_args.positional_args.iter().collect();
@ -106,10 +110,12 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
} }
} }
} else { } else {
let variant_name_str = format!("{variant_name}");
let variant_name_str = format!("{}", variant_name);
quote! { f.write_str(#variant_name_str)?; } quote! { f.write_str(#variant_name_str)?; }
}; };
match &variant.fields { match &variant.fields {
Fields::Unit => { Fields::Unit => {
quote! { quote! {
@ -121,19 +127,24 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
.unnamed .unnamed
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, _)| Ident::new(&format!("_{i}"), variant.span())) .map(|(i, _)| Ident::new(&format!("_{}", i), variant.span()))
.collect(); .collect();
if let Some(attr) = os_display_attr {
if let Ok(OsDisplayAttribute::Transparent) = attr.parse_args_with(OsDisplayAttribute::parse) {
quote! { let should_capture_value = if let Some(attr) = os_display_attr {
#name::#variant_name(value) => { #format_tokens } if let Ok(parsed) = attr.parse_args_with(OsDisplayAttribute::parse) {
} matches!(parsed, OsDisplayAttribute::Transparent | OsDisplayAttribute::FromDisplay)
} else { } else { false }
quote! { } else { false };
#name::#variant_name(#(#field_idents),*) => { #format_tokens }
} if should_capture_value {
quote! {
#name::#variant_name(value) => { #format_tokens }
} }
} else { } else {
quote! { quote! {
#name::#variant_name(#(#field_idents),*) => { #format_tokens } #name::#variant_name(#(#field_idents),*) => { #format_tokens }
} }
@ -145,18 +156,21 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
.iter() .iter()
.map(|f| f.ident.as_ref().unwrap().clone()) .map(|f| f.ident.as_ref().unwrap().clone())
.collect(); .collect();
if let Some(attr) = os_display_attr {
if let Ok(OsDisplayAttribute::Transparent) = attr.parse_args_with(OsDisplayAttribute::parse) { let should_capture_value = if let Some(attr) = os_display_attr {
let field_ident = fields.named.first().unwrap().ident.as_ref().unwrap(); if let Ok(parsed) = attr.parse_args_with(OsDisplayAttribute::parse) {
quote! { matches!(parsed, OsDisplayAttribute::Transparent | OsDisplayAttribute::FromDisplay)
#name::#variant_name{#field_ident} => { #format_tokens } } else { false }
} } else { false };
} else {
quote! { if should_capture_value {
#name::#variant_name{#(#field_idents),*} => { #format_tokens }
} let field_ident = fields.named.first().unwrap().ident.as_ref().unwrap();
quote! {
#name::#variant_name{#field_ident} => { #format_tokens }
} }
} else { } else {
quote! { quote! {
#name::#variant_name{#(#field_idents),*} => { #format_tokens } #name::#variant_name{#(#field_idents),*} => { #format_tokens }
} }
@ -190,6 +204,7 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
match parsed_attr { match parsed_attr {
OsDisplayAttribute::Transparent => { OsDisplayAttribute::Transparent => {
match &data_struct.fields { match &data_struct.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
quote! { quote! {
@ -218,15 +233,19 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
} }
} }
OsDisplayAttribute::FromDisplay => { OsDisplayAttribute::FromDisplay => {
quote! { quote! {
impl #impl_generics my_os_traits::OsDisplay for #name #ty_generics #where_clause { impl #impl_generics osstr_traits::OsDisplay for #name #ty_generics #where_clause {
fn fmt_os(&self, f: &mut my_os_traits::OsStringFormatter) -> std::fmt::Result { fn fmt_os(&self, f: &mut osstr_traits::OsStringFormatter) -> std::fmt::Result {
f.write_str(&self.to_string()) f.write_str(&self.to_string())
} }
} }
} }
} }
OsDisplayAttribute::Format(format_args) => { OsDisplayAttribute::Format(format_args) => {
let format_str_value = format_args.format_string.value(); let format_str_value = format_args.format_string.value();
let positional_expressions: Vec<&Expr> = format_args.positional_args.iter().collect(); let positional_expressions: Vec<&Expr> = format_args.positional_args.iter().collect();
let named_expressions: HashMap<String, Expr> = format_args.named_args let named_expressions: HashMap<String, Expr> = format_args.named_args
@ -249,7 +268,7 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
} }
}, },
Fields::Unnamed(fields) => { Fields::Unnamed(fields) => {
let idents: Vec<Ident> = fields.unnamed.iter().enumerate().map(|(i, _)| Ident::new(&format!("_{i}"), name.span())).collect(); let idents: Vec<Ident> = fields.unnamed.iter().enumerate().map(|(i, _)| Ident::new(&format!("_{}", i), name.span())).collect();
quote! { quote! {
let Self(#(#idents),*) = self; let Self(#(#idents),*) = self;
} }
@ -269,8 +288,9 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
} }
} }
} else { } else {
quote_spanned! {name.span() => quote_spanned! {name.span() =>
compile_error!("OsDisplay derive macro is not yet implemented for structs without #[os_display] attribute. Consider adding #[os_display(transparent)] for newtypes or specifying a format string using #[os_display(\"...\")] syntax."); compile_error!("OsDisplay derive macro is not yet implemented for structs without an #[os_display] attribute. Consider adding #[os_display(transparent)], #[os_display(from_display)], or specifying a format string using #[os_display(\"...\")] syntax.");
} }
} }
} }
@ -284,6 +304,7 @@ pub fn os_display_derive(input: TokenStream) -> TokenStream {
os_display_impl.into() os_display_impl.into()
} }
fn parse_os_display_format_string( fn parse_os_display_format_string(
format_str: &str, format_str: &str,
positional_expressions: &[&syn::Expr], positional_expressions: &[&syn::Expr],
@ -308,8 +329,7 @@ fn parse_os_display_format_string(
} }
let mut placeholder_content = String::new(); let mut placeholder_content = String::new();
while let Some(p) = chars.next() {
for p in &mut chars {
if p == '}' { if p == '}' {
break; break;
} }
@ -381,6 +401,6 @@ fn parse_os_display_format_string(
compile_error!("Too many positional arguments for format string: unused arguments provided."); compile_error!("Too many positional arguments for format string: unused arguments provided.");
}; };
} }
quote! { #(#generated_code_parts)* } quote! { #(#generated_code_parts)* }
} }