Crate optfield[−][src]
optfield
is a macro that, given a struct, generates another struct with
the same fields, but wrapped in Option<T>
.
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> }
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 |