Skip to content

Support for user-defined merge-strategies #175

Description

@JeanMertz

Sometimes you want users to be able to define in their configuration how a certain option should merge with existing values.

For example, if I have:

#[derive(Config)]
struct MyConfig {
    #[setting(nested, merge = schematic::merge::replace)]
    items: Vec<Item>,
}

I want the user to be able to determine how two MyConfig::items values should be merged by Schematic instead of hard-coding it to replace.

One solution I had in mind was to allow deserializing a wrapper enum, where one deserializes from a regular Vec, or from an object that includes the merge strategy:

#[derive(Config)]
struct MyConfig {
    #[setting(nested)]
    items: ReplaceOrMergedVec<Item>,
}

#[derive(Serialize, Deserialize, Config)]
#[serde(untagged)]
pub enum ReplaceOrMergedVec<T: Config> {
    #[setting(nested, merge = schematic::merge::replace)]
    Replace(Vec<T>),
    #[setting(merge = merge_vec_with_strategy)]
    Merged(MergedVec<T>),
}

#[derive(Serialize, Deserialize)]
pub struct MergedVec<T: Config> {
    strategy: Strategy,
    items: Vec<T>
}

#[derive(Serialize, Deserialize)]
pub enum Strategy {
    Append,
    Prepend,
    Replace,
}

fn merge_vec_with_strategy<T: Config>(
    mut prev: MergedVec<T>,
    next: MergedVec<T>,
    context: &(),
) -> MergeResult<MergedVec<T>> {
    match prev.strategy {
        // ...
    }
}

I ran into two issues with this:

  1. It looks like Schematic does not support generic types on configuration structs.
  2. You cannot define the merge proc-macro attribute on a nested non-collection item in a struct.

I can work around (1) by not using generics here (although it would definitely be nice), but that second feature is required because you need access to the strategy value do know how to merge the items.

I understand that there's the possibility of using Context for this, but it's not closely tied to specific fields of the configuration struct(s), and also would not be set through the configuration files themselves, so it's not really suitable for this use-case.

Any thoughts on supporting these two features, or perhaps supporting user-defined merge strategies differently?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions