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
Option
fields - 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
- The macro