Macros
The macros rust crate that contain (currenly) single derive macro - Property
.
Usually this crate uses as helper for writing a bunch of boilerplate code (e.g.
parsing config values).
Below you’ll see the description of macros from this crate.
Property derive macro
The macro which uses clean config struct and creates new struct with field types
wrapped by Option<T>
.
Let we pick a struct and attach the macro:
It will create a new struct in background:
The second struct will be helpful if you want to parse the values using serde
crate. So if you need to attach the derive macros to new struct, need to use
derive
argument of property
attribute. The changes:
As you can see, there is also Clone
and Default
derive traits. It is intentional,
because the new method of this trait requires that the struct can be cloned. And
the Default
trait uses for unwrapping values (it will be described below).
But it won’t compile and work because we didn’t mark fields that can be default:
Now the code should be able to compile.
Alright, except this, the macro also provide the methods for new struct which must
help reduce a bunch of boilerplate code. First and main method - unwrap_or_default(self) -> OriginalStruct
.
This method unwraps the fields and sets default values if these is None
.
It will look like that in generated code, considering to used attributes:
With it also implemented From<ConfigProperty> for Config
trait:
You can use unwrap_or_default(self)
and into()
together. It was made for ease about which
will be described below.
Also the created struct should be able to merge with other using merge(self, other: Option<Self>) -> Self
method. Note that the method consumes current value and creates new struct from both values.
Usually it more easier than use mutable reference, especially if there is submerges.
Merging struct looks like:
The examples were simple and understandable. But when you want to have a struct that have inner struct, like composion. In this examples, we’ll add the new struct:
But there is a problem. If we use AudioConfig
in Config
struct, the code will not
compile because the AudioConfig
doesn’t have Deserialize
derive trait. But using
AudioConfigProperty
in Config
struct is not a such good idea. And there is a solution:
As you see, the use_type
argument tells the macro that need to use AudioConfigProperty
type instead of AudioConfig
in ConfigProperty
struct. The main contract that for
the used type should be implemented a From<UsedType> for Type
trait. As I’ve written above,
the Property
macro autogenerates this trait, so you should’nt mind about it.
There is also detail about attribute - mergeable
. It tells the macro that there is a
type that have merge
method and use it if possible. For instance, you have the different
values for AudioConfig
from different sources but you want to merge them, then mergeable
is that what you need. And it merges AudioConfigProperty
from first struct with AudioConfigProperty
from second struct using merge
method of ConfigProperty
struct.
In last, there is also a way to verify values after all possible merging. In this context,
the verifier is not powerful method but extensible. And it doesn’t check the None
s in
sturct. This verifier was made for other purpose - verify values by complex algorithm and
return error if there is rude mistakes or do nothing if there is no issues.
To verify, use the verify(&self)
method. Initially this method is empty, because we didn’t
mark any fields by argument verifier(composite)
or verifier(path = path::to::function)
.
For example, you can verify two fields:
There is three different places of verifier
argument. For parameter
field it will call
crate::module::check_parameter
function with passing Option<&String>
value. The
function should have return type like Result<(), Box<dyn std::error::Error>
for easing
forwarding to main verify(&self)
function.
Also there is crate::audio::check_volume
which should be called in verify
method for
AudioConfig
. But there is a tricky part - the macro doesn’t know about the verify
method
of AudioConfig
and will not call it. So to tell it need to use verifier(composite)
that
tells the macro to use verify
method of concrete type (in this case - AudioConfig
).
Conclusions
Generated methods:
unwrap_or_default(self) -> OriginalStruct
- unwraps all fields or uses default values instead and converts into original struct.merge(self, other: Option<Self>) -> Self
- merges the current struct with other.verify(&self) -> Result<(), Box<dyn std::error::Error>
- verifies the struct.
Generated impls:
impl From<Property> to Origin
Possible attributes:
default
,default(expression)
ordefault(path = path::to::function)
- marker that tells that the field can have default value usingDefault
trait, expression or function respectively.use_type(TypeName)
- uses other type that canInto
to field.mergeable
- marker that tells that the field can usemerge
inmerge
function of struct.verifier(composite)
,verifier(composite, force_check)
orverifier(path = path::to::function)
- marker that tells that the field should be verified by provided function or use inner verifier if it is composite.