~ireas/public-inbox

[merge-rs] Merge strategies for multi-level types

Details
Message ID
<20200921102712.GA1424@ireas.org>
DKIM signature
pass
Download raw message
Hi Joshua,

let’s continue this discussion on-list to make it easier to look it up
in the future.  The question was how to set merge strategies for
multi-level types, for example:

    struct TomlConfig {
        target: Option<HashMap<String, TomlTarget>>,
    }

Here, we would like to use the recurse strategy both for the Optiona dn
the HashMap.  You suggested using a wrapper newtype struct that derives
Merge, for example:

    #[derive(Merge)]
    #[merge(strategy = merge::hashmap::recurse)]
    struct TargetWrapper(HashMap<String, TomlTarget)>

    #[derive(Merge)]
    #[merge(strategy = merge::option::recurse)]
    struct TomlConfig {
        target: Option<TargetWrapper>,
    }

What I don’t like about this approach is that it affects all consumers
of the TomlConfig struct.  Also, adding these wrapper types to the merge
crate would most likely cause problems with the orphan rules.  Even in
your example, the wrapper type has to implement Deserialize and
Serialize.

My preferred approach with the current implementation is using a custom
merge strategy.  This way, other users of the struct are not affected
and the change is enclosed in the derive atributes.  But as far as I
see, you don’t like this approach, right?

I think we could make this easier by adding a wrap option to the merge
attribute:

    // in merge::option
    fn wrap<T, T: Fn(&mut T, T)>(left: &mut T, right: T, merge: F) {
        if let Some(right) = right {
            if let Some(left) = left {
	        merge(left, right);
	    } else {
                *left = right;
	    }
	}
    }

    #[derive(Merge)]
    struct TomlConfig {
        #[merge(strategy = merge::hashmap::recurse, wrap = merge::option::recurse)]
        target: Option<HashMap<String, TomlTarget>>,
    }
 
The derive macro would then call

    #wrap(&mut #self.name, other.#name, #strategy)

instead of

    #strategy(&mut #self.name, other.#name)

In theory, this could be extended to multiple levels of wrappers, but I
don’t want to consider that without a use case.

Note that I did not try to implement this, but I think it should work.
What do you think?

/Robin
Reply to thread Export thread (mbox)