I keep encountering this pattern which I have struggled to find a good solution.
- There is a global config with non-nullable fields
- Each item can override some per-item config fields
Example: RSS Reader
struct App {
config: Config, // global config
feeds: Vec<Feed>, // per-item config
}
struct Config {
max_retry: u8,
browser_agent: String,
fetch_interval: std::time::Duration,
}
struct OptionalConfig {
max_retry: Option<u8>,
browser_agent: Option<String>,
fetch_interval: Option<std::time::Duration>,
}
struct Feed {
source: Source,
config: OptionalConfig,
}
Here OptionalConfig feels too verbose.
Example: Theme
A nested config is even more complex. All intermediate and leaf attributes should be Option.
pub struct Theme {
pub title: LabelStyle,
pub subtitle: LabelStyle,
...
}
pub struct LabelStyle {
pub family: FamilyCow,
pub size: Logical<f32>,
pub line_height: Option<Logical<f32>>,
pub color: Color,
pub weight: u16,
}
struct Grid {
theme: Theme,
children: Vec<Box<Grid>>,
}
expands to
pub struct ThemeOverride {
pub title: Option<LabelStyleOverride>,
pub subtitle: Option<LabelStyleOverride>,
...
}
pub struct LabelStyleOverride {
pub family: Option<FamilyCow>,
pub size: Option<Logical<f32>>,
pub line_height: Option<Option<Logical<f32>>>,
pub color: Option<Color>,
pub weight: Option<u16>,
}
grid.theme = default_theme;
grid.children[0].theme = default_theme.override(theme_override);
This has many problems.
line_height: Option<Option<..>>is difficult to use and serialize. Also ambiguous when exposed through PyO3 or wasm-bindgen.ThemeOverrideis large even when there are only few fields to override.Themealready uses more than 30 types, and adding override types for each is too much.- The API for
grid.children[0].themeis bad; Updatinggrid.themedoesn't automatically update the child theme. Ideally the child item should only specify the difference, which will be computed by the library
Is there a better approach?