Expand description
optfield is a macro that, given a struct, generates another struct with
the same fields, but wrapped in Option<T>.
Features:
- Simple examples
- Visibility
- Rewrapping
Optionfields - Documentation
- Attributes
- Field documentation
- Field attributes
- Merging
- From
§Simple examples
The first argument is the name of the generated struct:
use optfield::optfield;
#[optfield(Opt)]
struct MyStruct {
text: String
}Will generate the following second struct (leaving MyStruct as is):
struct Opt {
text: Option<String>
}It also works with tuple structs:
#[optfield(Opt)]
struct MyTuple(String, i32);Will generate:
struct Opt(Option<String>, Option<i32>);Generics and lifetimes are preserved:
#[optfield(Opt)]
struct MyStruct<'a, T> {
field: &'a T
}Will generate:
struct Opt<'a, T> {
field: Option<&'a T>
}§Visibility
By default, opt structs are private. To use custom visibility simply add it right before the opt struct name:
#[optfield(pub(crate) Opt)]
struct MyStruct {
text: String
}Will generate:
pub(crate) struct Opt {
text: Option<String>
}Field visibility is preserved.
§Rewrapping Option fields
By default, fields that are already wrapped in Option<T> are not wrapped
again:
#[optfield(Opt)]
struct MyStruct {
text: Option<String>,
number: i32
}Will generate:
struct Opt {
text: Option<String>,
number: Option<i32>
}To rewrap them pass the rewrap argument:
#[optfield(Opt, rewrap)]
struct MyStruct {
text: Option<String>,
number: i32
}Will generate:
struct Opt {
text: Option<Option<String>>,
number: Option<i32>
}§Documentation
To document the opt struct, either duplicate the same documentation as the
original using the doc argument by itself:
/// My struct documentation
/// ...
#[optfield(Opt, doc)]
struct MyStruct {
text: String
}Will generate:
/// My struct documentation
/// ...
struct Opt {
text: Option<String>
}Or write custom documentation by giving doc a value:
#[optfield(
Opt,
doc = "
Custom documentation
for Opt struct...
"
)]
struct MyStruct {
text: String
}Will generate:
/// Custom documentation
/// for Opt struct...
struct Opt {
text: Option<String>
}§Attributes
The attrs argument alone makes optfield insert the same attributes as the
original:
#[optfield(Opt, attrs)]
#[cfg(test)]
#[derive(Clone)]
struct MyStruct {
text: String
}Will generate:
#[cfg(test)]
#[derive(Clone)]
struct Opt {
text: Option<String>
}To add more attributes besides the original ones, use attrs = add(...):
#[optfield(
Opt,
attrs = add(
cfg(test),
derive(Clone)
)
)]
#[derive(Debug)]
struct MyStruct {
text: String
}Will generate:
#[derive(Debug)]
#[cfg(test)]
#[derive(Clone)]
struct Opt {
text: Option<String>
}To replace with other attributes, attrs = (...):
#[optfield(
Opt,
attrs = (
cfg(test),
derive(Clone)
)
)]
#[derive(Debug)]
struct MyStruct {
text: String
}Will generate:
#[cfg(test)]
#[derive(Clone)]
struct Opt {
text: Option<String>
}NOTE on attribute order: optfield, like any other proc macro, only
sees the attributes defined after it:
#[cfg(test)] // optfield is unaware of this attribute
#[optfield(Opt, attrs)]
#[derive(Debug)]
struct MyStruct;Will generate:
#[derive(Debug)]
struct Opt;§Field documentation
By default, field documentation is removed:
#[optfield(Opt)]
struct MyStruct {
/// Field
/// documentation
text: String
}Will generate:
struct Opt {
text: Option<String>
}To preserve field documentation use the field_doc argument:
#[optfield(Opt, field_doc)]
struct MyStruct {
/// Field
/// documentation
text: String
}Will generate:
struct Opt {
/// Field
/// documentation
text: Option<String>
}§Field attributes
Field attributes can be handled using the field_attrs argument which works
similarly to attrs, but applies to all fields.
field_attrs can be used independently of attrs.
By default, no field attributes are inserted:
#[optfield(Opt, attrs)]
#[derive(Deserialize)]
struct MyStruct {
#[serde(rename = "text")]
my_text: String
}Will generate:
#[derive(Deserialize)]
struct Opt {
my_text: Option<String>
}To keep them:
#[optfield(Opt, attrs, field_attrs)]
#[derive(Deserialize)]
struct MyStruct {
#[serde(rename = "text")]
my_text: String
}Will generate:
#[derive(Deserialize)]
struct Opt {
#[serde(rename = "text")]
my_text: Option<String>
}To add more attributes:
#[optfield(
Opt,
attrs,
field_attrs = add(
serde(default)
)
)]
#[derive(Deserialize)]
struct MyStruct {
#[serde(rename = "text")]
my_text: String,
#[serde(rename = "number")]
my_number: i32
}Will generate:
#[derive(Deserialize)]
struct Opt {
#[serde(rename = "text")]
#[serde(default)]
my_text: Option<String>,
#[serde(rename = "number")]
#[serde(default)]
my_number: Option<i32>
}To replace all field attributes:
#[optfield(
Opt,
attrs,
field_attrs = (
serde(default)
)
)]
#[derive(Deserialize)]
struct MyStruct {
#[serde(rename = "text")]
my_text: String,
#[serde(rename = "number")]
my_number: i32
}Will generate:
#[derive(Deserialize)]
struct Opt {
#[serde(default)]
my_text: Option<String>,
#[serde(default)]
my_number: Option<i32>
}§Merging
When the merge_fn argument is used optfield will add a method to the
original struct that merges an opt struct back into the original.
By default, the method is named merge_opt and has the following signature:
// assuming the opt struct is named Opt;
// takes opt by value;
fn merge_opt(&mut self, opt: Opt)When merging, all values of the opt struct that are Some(...) are set as
values of the original struct fields.
To use it:
#[optfield(Opt, merge_fn)]
struct MyStruct {
text: String,
number: i32
}
let mut original = MyStruct {
text: "awesome".to_string(),
number: 1
};
let opt = Opt {
text: Some("amazing".to_string()),
number: None
};
original.merge_opt(opt);
// text field value is merged
assert_eq!(original.text, "amazing");
// number field stays the same
assert_eq!(original.number, 1);The merge function can be given:
- custom name:
merge_fn = my_merge_fn - custom visibility (default is private):
merge_fn = pub(crate) - both:
merge_fn = pub my_merge_fn
§From
When the from argument is used, From<MyStruct> is implemented for Opt.
#[optfield(Opt, from)]
struct MyStruct {
text: String,
number: i32,
}
let original = MyStruct {
text: "super".to_string(),
number: 2,
};
let from = Opt::from(original);
assert_eq!(from.text.unwrap(), "super");
assert_eq!(from.number.unwrap(), 2);Attribute Macros§
- optfield
- The macro