moonrepo / moonrepo/schematic

Support for user-defined merge-strategies

Open
#175 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
93
Forks
9
Avg merge
10m
Merged PRs (30d)
6

Description

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?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing how the Config derive proc macro handles generic configuration types and the merge attribute on nested non-collection fields. Compare that behavior with the Context mechanism described in the issue. Done means a documented, supported way to define a field-specific merge strategy that can access the strategy value from configuration data.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.