2 releases
0.1.11 | Aug 22, 2024 |
---|---|
0.1.10 | Jul 15, 2024 |
#1575 in Procedural macros
21KB
265 lines
FieldnameAccess Derive Macro
It is used to safely get values from the structure by field name when we do not know exactly which field we will need at the moment but can match it and do some actions based on other data.
Also it generates const FIELDS: [&'static str; FIELDS_COUNT]
constant with struct fields and field_iter
method on struct for creating Iterator
over struct using generated field enum.
Container attributes
#fieldname_enum(name = "NewName")
- Name of generated enum of possible values
use fieldname_access::FieldnameAccess;
#[derive(FieldnameAccess, Default)]
#[fieldname_enum(name = "NewName")]
struct NamedFieldname {
name: String,
age: i64,
}
let mut instance = NamedFieldname::default();
match instance.field("name").unwrap() {
NewName::String(val) => {}
NewName::I64(val) => {},
}
match instance.field_mut("name").unwrap() {
NewNameMut::String(val) => {}
NewNameMut::I64(val) => {},
}
#fieldname_enum(derive = [Debug, Clone], derive_mut = [Debug])
- Derive macroses for generated enums.derive
only for enum with immutable references,derive_mut
only for enum with mutable references. It can be helpful when you want to deriveClone
but only for immutable references as mutable are not clonable
use fieldname_access::FieldnameAccess;
#[derive(FieldnameAccess)]
#[fieldname_enum(derive = [Debug, Clone], derive_mut = [Debug])]
struct NamedFieldname {
name: String,
age: i64,
}
#fieldname_enum(derive_all = [Debug])
- Derive macroses for immutable and mutable generated enums
use fieldname_access::FieldnameAccess;
#[derive(FieldnameAccess)]
#[fieldname_enum(derive_all = [Debug])]
struct NamedFieldname {
name: String,
age: i64,
}
Field attributes
#fieldname = "AmazingAge"
- Name of variant for field in generated enum. It can be helpfull when you want to 'mark' field with specific variant name
use fieldname_access::FieldnameAccess;
#[derive(FieldnameAccess, Default)]
struct NamedFieldname {
name: String,
#[fieldname = "MyAge"]
age: i64,
dog_age: i64
}
let mut instance = NamedFieldname::default();
match instance.field("name").unwrap() {
NamedFieldnameField::String(val) => {}
NamedFieldnameField::MyAge(val) => {}
NamedFieldnameField::I64(val) => {}
}
match instance.field_mut("name").unwrap() {
NamedFieldnameFieldMut::String(val) => {}
NamedFieldnameFieldMut::MyAge(val) => {}
NamedFieldnameFieldMut::I64(val) => {}
}
Practical example
Let's say we have a User structure and Crit criteria for it.
use fieldname_access::FieldnameAccess;
#[derive(FieldnameAccess)]
#[fieldname_enum(derive_all = [Debug])]
struct User {
name: String,
age: u64,
does_love_ranni: bool, // important
}
struct Crit {
value: String,
field: String,
kind: CritKind,
}
enum CritKind {
Contains,
Equals,
BiggerThan,
}
Based on this information FieldnameAccess
will generate all possible types
of field and methods for User
struct to access them by name:
enum UserField<'a> {
String(&'a String),
U64(&'a u64),
Bool(&'a bool)
}
enum UserFieldMut<'a> {
String(&'a mut String),
U64(&'a mut u64),
Bool(&'a mut bool)
}
Having information on these two elements, we can determine our next steps.
let user = User {
age: 2022,
name: String::from("Radahn"),
does_love_ranni: true,
};
let crits = vec![
Crit {
value: String::from("Ra"),
field: String::from("name"),
kind: CritKind::Contains,
},
Crit {
value: String::from("true"),
field: String::from("does_love_ranni"), // important
kind: CritKind::Equals,
},
Crit {
value: String::from("18"),
field: String::from("age"),
kind: CritKind::BiggerThan,
},
];
let its_ok = crits.into_iter().all(|crit| {
let user_field =
user.field(&crit.field).expect("Criteria has wrong fieldname");
match crit.kind {
CritKind::Contains => match user_field {
UserField::String(str) => str.contains(&crit.value),
_ => panic!("Criteria has wrong value"),
},
CritKind::Equals => match user_field {
UserField::String(str) => str.eq(&crit.value),
UserField::U64(int) => int.eq(&crit.value.parse::<u64>().unwrap()),
UserField::Bool(boolean) => {
boolean.eq(&crit.value.parse::<bool>().unwrap())
}
},
CritKind::BiggerThan => match user_field {
UserField::U64(int) => int > &crit.value.parse::<u64>().unwrap(),
_ => panic!("Criteria has wrong value"),
},
}
});
assert!(its_ok);
Also you can modify fields
if let Some(UserFieldMut::Bool(does_love_ranni)) =
user.field_mut("does_love_ranni") // important
{
*does_love_ranni = false; // HARAM
}
assert!(!user.does_love_ranni); //important
And iterate over struct fields
let not_so_pretty_output = user
.field_iter()
.map(|(field_name, enum_var)| format!("{}={:?}", field_name, enum_var))
.join("\n");
println!("Here it is {}", not_so_pretty_output);
Dependencies
~2.5MB
~53K SLoC