diff --git a/strinto/.gitignore b/strinto/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/strinto/.gitignore @@ -0,0 +1 @@ +/target diff --git a/strinto/Cargo.lock b/strinto/Cargo.lock new file mode 100644 index 0000000..7f8aab3 --- /dev/null +++ b/strinto/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strinto" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/strinto/Cargo.toml b/strinto/Cargo.toml new file mode 100644 index 0000000..d47f7cc --- /dev/null +++ b/strinto/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "strinto" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "2.0", features = ["full"] } +quote = "1.0" + +[dev-dependencies] +proc-macro2 = "1.0" diff --git a/strinto/src/lib.rs b/strinto/src/lib.rs new file mode 100644 index 0000000..b147e4d --- /dev/null +++ b/strinto/src/lib.rs @@ -0,0 +1,62 @@ +extern crate proc_macro; +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Expr, ExprStruct}; + +/// # literal strings in a struct insantiation are converted .into(). +/// +/// ```rust +/// use strinto::strinto; +/// #[derive(Debug)] +/// struct TestStruct { +/// name: String, +/// title: String, +/// description: String, +/// age: usize, +/// } +/// +/// let descr = "description"; +/// +/// let output = strinto!(TestStruct { +/// name: "John", // Literal string. +/// title: String::from("Wicked"), +/// description: descr.to_string(), +/// age: 30, +/// }); +/// +/// let output_string = format!("{:?}", output); +/// assert_eq!( +/// output_string, +/// "TestStruct { name: \"John\", title: \"Wicked\", description: \"description\", age: 30 }" +/// ); +/// ``` + +#[proc_macro] +pub fn strinto(input: TokenStream) -> TokenStream { + let expr_struct = parse_macro_input!(input as ExprStruct); + + // Extract struct name and fields + let struct_name = &expr_struct.path; + let fields = expr_struct.fields.iter().map(|field| { + let field_name = field.member.clone(); + let field_value = &field.expr; + // Determine if the field value is a string literal and transform it + if let Expr::Lit(expr_lit) = field_value { + if let syn::Lit::Str(_) = expr_lit.lit { + quote! { #field_name: #field_value.into() } + } else { + quote! { #field_name: #field_value } + } + } else { + quote! { #field_name: #field_value } + } + }); + + let expanded = quote! { + #struct_name { + #(#fields,)* + } + }; + + expanded.into() +}