code: side-feature: exploring string into solution. will probably be moved away
This commit is contained in:
parent
7923d0a957
commit
2c088d03ce
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -1258,10 +1258,19 @@ dependencies = [
|
||||
"regex",
|
||||
"rodio",
|
||||
"serde",
|
||||
"strinto",
|
||||
"tokio",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strinto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia"
|
||||
version = "0.5.3"
|
||||
|
@ -21,3 +21,4 @@ rodio = "0.17.1"
|
||||
serde = { version = "1.0.171", features = ["derive"] }
|
||||
tokio = { version = "1.29.1", features = ["full"] }
|
||||
xxhash-rust = { version = "0.8.6", features = ["xxh3", "const_xxh3"] }
|
||||
strinto = { path = "./strinto" }
|
||||
|
30
src/bin/into.rs
Normal file
30
src/bin/into.rs
Normal file
@ -0,0 +1,30 @@
|
||||
#[macro_export]
|
||||
macro_rules! into {
|
||||
($struct:ident { $($field:ident : $value:expr),* $(,)? }) => {
|
||||
$struct {
|
||||
$(
|
||||
$field: $value.into(),
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct SomeStruct {
|
||||
first_name: String,
|
||||
last_name: String,
|
||||
age: usize,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let last_name: String = "Last".to_string();
|
||||
let age: usize = 30;
|
||||
|
||||
let x = into!(SomeStruct {
|
||||
first_name: "First",
|
||||
last_name: last_name,
|
||||
age: age,
|
||||
});
|
||||
}
|
||||
|
||||
// see cargo expand to see how this function changes all struct inits to .into().
|
||||
// see also strinto, if you think "Why could it not be done in a macro as condition?"
|
68
src/bin/strinto_declarative.rs
Normal file
68
src/bin/strinto_declarative.rs
Normal file
@ -0,0 +1,68 @@
|
||||
///
|
||||
/// Trying to get around "".into() for String values.
|
||||
/// Or "".to_owned().
|
||||
/// Or String::from("").
|
||||
/// Or "".to_string().
|
||||
/// Choose your church.
|
||||
///
|
||||
/// This is as far as you will get declaratively.
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! strinto {
|
||||
($struct:ident { $($field:ident : $value:expr),* $(,)? }) => {
|
||||
$struct {
|
||||
$(
|
||||
$field: $crate::strinto!(@convert $value),
|
||||
)*
|
||||
}
|
||||
};
|
||||
|
||||
(@convert $value:literal) => {
|
||||
match () {
|
||||
_ if stringify!($value).starts_with("\"") => {
|
||||
$value.to_string()
|
||||
},
|
||||
_ => $value.into(), // <-- no getting rid of the into!
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct SomeStruct {
|
||||
first_name: String,
|
||||
//last_name: String, // NOPE because of @convert.
|
||||
//age: usize, // reason of .into() in the first place.
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let x = strinto!(SomeStruct {
|
||||
first_name: "First",
|
||||
//last_name: String::from("Last"), // NOPE 2.
|
||||
//age: 1, // NOPE 1. But I went further.
|
||||
});
|
||||
}
|
||||
|
||||
// while this compiles for only &str "", it is also useless compared to into!
|
||||
// the reason is, that you cannot type check in the declarative macros.
|
||||
// while yes, you would conditionally run stringify!($value) in the match, but the match expansion
|
||||
// will always lead you to type errors if you don't do an .into() in the other arm.
|
||||
// also in the end it fails to compile for anything but all members of the struct being Strings.
|
||||
|
||||
// the last idea was going with a helper trait, but that will bleed into the runtime code, and yet again only work on pure String structs.
|
||||
|
||||
// I guess I have to embrace String::from(), .to_string(), .to_owned(), .into() madness, for something every human reader can deduce in a second,
|
||||
// if you would just implicitly convert &str to String if literally written in the code.
|
||||
|
||||
// It is kinda amusing, that the other solution, to use new() kind of turns me off, because I cannot be explicit about the parameter name in the call.
|
||||
// I would literally love the option to be more explicit in function param names.
|
||||
|
||||
// while builder patterns feel a bit bloated for static runtime options, i will probably look into automations for that.
|
||||
|
||||
// this is probably the only aesthetic decision in rust I probably will hate forever.
|
||||
// It does not make sense, Strings as literals already are special in your code.
|
||||
// Because numbers are as well, you dont have to write 123.into() either.
|
||||
// I know I probably made some really harsh logical mistakes in my opinion here, and maybe it can be proven, that I am wrong, and I would love to hear that
|
||||
// However it kind of feels like an excuse to not simplify assigning declaratively written &str to Strings in the code.
|
||||
|
||||
// And it makes sense to be explicit about creating a String Buffer sometimes, but it does not make sense mostly.
|
||||
|
||||
// Anyway, I will still try a procedural macro for this, just for fun.
|
1
strinto/.gitignore
vendored
Normal file
1
strinto/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
47
strinto/Cargo.lock
generated
Normal file
47
strinto/Cargo.lock
generated
Normal file
@ -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"
|
14
strinto/Cargo.toml
Normal file
14
strinto/Cargo.toml
Normal file
@ -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"
|
62
strinto/src/lib.rs
Normal file
62
strinto/src/lib.rs
Normal file
@ -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()
|
||||
}
|
Loading…
Reference in New Issue
Block a user