I have been working on configuration parsing code and wondered if you could help me to pick the best types for this code.
I am trying to parse the following TOML:
[default]
a=10
b="abc"
[section1]
a = 78
[section2]
b="xyz"
The types of keys are the same in each section and each field follows the chain of defaults: sectionX.value
=> default.value
=> default value hardcoded in Rust
via x.value.or(default.value).or(Some(...)
for each field.
The most straightforward way to declare it in Rust (including serde attributes)
struct Section{
a: Option<usize>,
b: Option<String>,
}
The problem is that I want to parse all defaults first, and then use a fully materialized struct
with no unassigned values in my code.
struct Section{
a: usize,
b: String,
}
I can use the following approaches:
Use original
Section
struct and always tounwrap
/expect
because I "know" the defaults have been assigned in the config parsing code. I make a mistake, I can catch panic and that does not look tidy. I'd like to leverage more help from the compiler.Use the second
Section
struct (the one that has noOptions
). I can assign defaults via Serde annotations, but then I am loosing the signal that something was unspecified and needs a default from another level.Declare both variants of the struct. This is the most verbose variant and I will look even more verbose when I grow 20+ fields or embedded structs in the config.
Generated Solution 3 via macros or some clever typing. Is there a crate for this? Maybe
Section
could have some generic type that can beOption
in one place, but then "newtype" wrapper with a single value somewhere else?Something else?
Some of the solutions above would work alright, but maybe there is a known elegant solution.