-
-
Notifications
You must be signed in to change notification settings - Fork 652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inline multiple config files into options sources. #20549
Conversation
@@ -216,133 +215,16 @@ fn toml_table_to_dict(table: &Value) -> HashMap<String, Val> { | |||
} | |||
} | |||
|
|||
#[derive(Clone)] | |||
struct ConfigSource { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We get rid of ConfigSource entirely, and fold its functionality directly into Config
below, since they are now one-to-one.
}) | ||
} | ||
|
||
fn option_name(id: &OptionId) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved from ConfigSource
let mut edits: Vec<ListEdit<T>> = vec![]; | ||
for source in self.sources.iter() { | ||
edits.append(&mut source.get_list(id, parse_list)?); | ||
let mut list_edits = vec![]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved from ConfigSource
@@ -55,49 +48,6 @@ fn test_display() { | |||
); | |||
} | |||
|
|||
#[test] | |||
fn test_multiple_sources() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple configs is no longer a thing to be tested separately within config_tests.rs, it is amply tested along with multiple sources in general, in tests.rs.
@@ -185,16 +137,25 @@ fn test_interpolate_config() { | |||
.unwrap() | |||
); | |||
|
|||
// TODO: Uncomment when we implement get_dict. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have done this before. It needed a little massaging after uncommenting.
} | ||
|
||
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] | ||
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ord will do the right thing here: order by increasing declaration order, and within Config, lexicographically by field, hence ordinal
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't notice a test for the ord behaviour; have I missed it and/or is it worth adding one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It falls out of the documentation for the generated Ord, but can't hurt to add one, done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking the test would be catching if someone jumps in and changes the enum (eg sorts the variants alphabetically) they may not realise that the variant and field order is specifically meaningful, rather than any order being fine (and just being necessary to allow use in BTreeMap or similar).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, a test is a good idea, added one.
} | ||
} | ||
|
||
fn path_strip(prefix: &str, path: &str) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For paths under the buildroot, it seemed more useful to put the relative path to the buildroot in the derivation, rather than the full path. It also makes the tests simpler.
Note that we implement our own simply path manipulation here because we have Strings in hand, not OsString or Path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One can construct paths from strs cheaply: https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.new
Did you consider that as an option?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is going back to a String from the underlying OsString, but for now I've done it with an aggressive unwrap(), and in the future the calling code should really be using OsString or Path anyway.
if let Some(val) = getter(source, id)? { | ||
derivations.push((*source_type, val)); | ||
derivations.push((source_type.clone(), val)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now the derivations will contain many hundreds of copies of the string pants.toml
or whatever. In a followup I might change this to a &str
with a lifetime tied to the OptionsParser, so we can re-use references.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the lifetime is too annoying/constraining, an Rc<String>
or Rc<str>
(or Arc
) might be options to consider too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would have to be Arc because these get passed into Python code, but yeah, not a bad solution.
It's a non-trivial number of diff lines, but a big chunk of that is moving code from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool. Seems to be a nice conceptual simplification!
src/rust/engine/options/src/lib.rs
Outdated
|
||
fn path_strip(prefix: &str, path: &str) -> String { | ||
match path.strip_prefix(prefix) { | ||
Some(suffix) => match suffix.strip_prefix(path::MAIN_SEPARATOR) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just confirming that this separator stripping should only apply if the prefix was removed? (That is, if the prefix remains, a trailing / should too?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, to ensure that the stripped dir is a relpath.
src/rust/engine/options/src/lib.rs
Outdated
} | ||
|
||
fn path_strip(prefix: &str, path: &str) -> String { | ||
match path.strip_prefix(prefix) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this would strip off a prefix in the middle of a directory, like path = /abcdef
, prefix = /abc
=> def
. Is that desirable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, yeah, that is not good.
I think I will switch to using Path
utils.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to using Path
.
} | ||
} | ||
|
||
fn path_strip(prefix: &str, path: &str) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One can construct paths from strs cheaply: https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.new
Did you consider that as an option?
if let Some(val) = getter(source, id)? { | ||
derivations.push((*source_type, val)); | ||
derivations.push((source_type.clone(), val)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the lifetime is too annoying/constraining, an Rc<String>
or Rc<str>
(or Arc
) might be options to consider too.
} | ||
|
||
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] | ||
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't notice a test for the ord behaviour; have I missed it and/or is it worth adding one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great
Previously the options sources list could have only one source
per type (flag/env/config/default). Since we support multiple
config files, we had the concept of a "merged config" that
encompassed multiple "config sources". This led to
duplication of the logic that traverses and merges sources.
This change gets rid of the concept of merged config.
Instead, the list of options sources we consult can now
include multiple config files. This is achieved by enhancing
the Source::Config enum to include an ordinal (to ensure
that configs are applied in the right order) and the path to
the config file (purely informational).
The natural
#[derive(Ord)]
will order these augmentedconfigs appropriately within all the sources. To make this
easier to achieve we change the order of the enum to be
from lowest to highest priority, so that we can add config
files naturally in the order in which we encounter them.
This also allows us to simplify some types. In particular,
we no longer need to traffic in
Vec<DictEdit>
- a singlesource can only return a single
DictEdit
.More importantly, this supports derivation history that
includes the specific config file each value comes from.