OptionalStruct
Quick-start
From the tests/builder.rs file:
use *;
Goal
Since rust does not have default arguments, and some tools are strict when deserializing data (e.g. serde), missing configuration values can be quite frustrating to deal with. For example:
If we read the configuration from a file, and the log_file is not specified,
serde will fail to create the struct. While serde offers ways to set the
default value for a field with e.g.
there are obvious limitations. This crate aims to fill this gap by allowing optional values, and providing an easy way to apply values obtained from different sources to construct our configuration.
With optional_struct, one can define the required
configuration as it shall be used and only use the generated struct
to handle configuration/missing values/default values.
How
The macro optional_struct generates a structure containing the same fields as the one it was tagged on, but wrapped by an Option.
A function on the new structure allows applying its values to the original one
(if the Options are not None). This can be called multiple times, to apply
configuration from different source, while giving the caller complete control
over how to set the values, since the generated struct can be easily manipulated
and passed around before constructing the final configuration.
Features
- Rename the generated struct:
;
- Handle recursive types:
- Handle
Options in the original struct (by ignoring them):
- Force wrapping (or not) of fields:
- Change the default wrapping behavior:
- Add serde's
skip_serializing_if = "Option::is_none"attribute to generated struct
By adding the attribute #[optional_serde_skip_none] to a field, the generated
struct will have the same field tagged #[serde(skip_serializing_if = "Option::is_none")].
This attribute makes serde skip fields entirely if the value of the Option is
none (rather than saving e.g. "value" = null if serializing to json).
apply, build, and try_build
Those three functions are used to build the final version of the structure, by collapsing the values "on the left".
The signatures of the functions are (in pseudo-code):
What those functions do:
-
buildtakes a realStructand sets all its field based on which fields are set inOptionalStruct. Missing fields are left alone.Optionfields with a force-wrap attributes will NOT overwrite the value e.g.Some(1)will not overwriteSome(2)(see the initial example for a concrete situation. -
try_buildtries to build a wholeStructfrom theOptionalStruct, returning either anOk(Struct)if things went well, or the initialOptionalStructin theErr(OptionalStruct)in case things were missing. -
applytakes anOptionalStructas a parameter and applies its fields to the left (i.e.self). Ifselfandotherboth define something, the value fromotheris taken. Ifselfdefines something but notother, the value is preserved. Naturally, ifselfdoes not define something butotherdoes, this value is used.