Figment icon indicating copy to clipboard operation
Figment copied to clipboard

Best way to support an "extends other config" field

Open milesj opened this issue 2 years ago • 1 comments

@SergioBenitez

Many tools and their config files support the concept of extending additional config files through a field within the file, with the values being a relative file system path (extends: './other/config.yml') or URL (extends: 'https://domain.com/config.yml'). With this approach, the extends file is applied before the file doing the extending, so that the current document can override or merge when necessary. This is pretty great for composibility and reusability.

However, this seems to be extremely difficult in Figment, and I'm curious on the best way to approach it. The current problems are:

  • We can't use merge() or join() because we don't know what file to extend until after the config has been parsed and extracted.
  • We can't use a provider since it doesn't have access to the currently parsed config from the previous provider.
  • We can't use a profile because it still needs a way to be resolved.

I have a working solution to this problem but it requires resolving and parsing the config twice, as demonstrated here: https://github.com/moonrepo/moon/pull/142/files#diff-752c2babea244e138a59a074967c9c3bb0faf51bf0a413ac4128505e2b58fb7fR146 The second figment extraction contains an additional merge() for the extending file (which is requested via URL).

In an ideal world, something like this would be pretty great.

Figment::from(Serialized::defaults(WorkspaceConfig::default()))
    .merge(Yaml::file(&path))
    .merge_first(Yaml::from_field("extends"))
    .extract()

milesj avatar Jun 16 '22 01:06 milesj

Since a Figment is itself a Provider, I wonder if we need any special support for this. How does this look?

let mut fig = Figment::from(Yaml::file(path));
if let Ok(path) = layer.find_value("extends").map(|v| v.as_str()) {
    fig = Figment::from(Yaml::file(path)).merge(fig);
}

fig.extract()

If you want path to be relative to where it was declared, then use the value.tag() to retrieve the location via Figment.get_metadata() and make the path relative to it using figment::util::diff_paths.

SergioBenitez avatar Jun 18 '22 21:06 SergioBenitez

Closing due to inactivity.

SergioBenitez avatar Sep 06 '22 07:09 SergioBenitez