Skip to content

Commit

Permalink
Support None option values. (#20736)
Browse files Browse the repository at this point in the history
The python options parser allows default=None,
so we must support it in the rust parser.

A None value can currently only be obtained
via a default of None: if a default is not None
then the option will have that value if it doesn't
take a non-None value from some other source.

We don't currently support setting a value to None in
args/env/config in Python, so we don't support
it here either. However the data structures could
support this if we want to in the future.
  • Loading branch information
benjyw authored Apr 2, 2024
1 parent d127c83 commit 5374772
Showing 1 changed file with 67 additions and 11 deletions.
78 changes: 67 additions & 11 deletions src/rust/engine/options/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,23 @@ pub struct OptionValue<T> {
pub value: T,
}

#[derive(Debug)]
pub struct OptionalOptionValue<T> {
pub derivation: Option<Vec<(Source, T)>>,
pub source: Source,
pub value: Option<T>,
}

impl<T> OptionalOptionValue<T> {
fn unwrap(self) -> OptionValue<T> {
OptionValue {
derivation: self.derivation,
source: self.source,
value: self.value.unwrap(),
}
}
}

#[derive(Debug)]
pub struct ListOptionValue<T> {
pub derivation: Option<Vec<(Source, Vec<ListEdit<T>>)>>,
Expand Down Expand Up @@ -357,12 +374,15 @@ impl OptionParser {
fn parse_scalar<T: ToOwned + ?Sized>(
&self,
id: &OptionId,
default: &T,
default: Option<&T>,
getter: fn(&Rc<dyn OptionsSource>, &OptionId) -> Result<Option<T::Owned>, String>,
) -> Result<OptionValue<T::Owned>, String> {
) -> Result<OptionalOptionValue<T::Owned>, String> {
let mut derivation = None;
if self.include_derivation {
let mut derivations = vec![(Source::Default, default.to_owned())];
let mut derivations = vec![];
if let Some(def) = default {
derivations.push((Source::Default, def.to_owned()));
}
for (source_type, source) in self.sources.iter() {
if let Some(val) = getter(source, id)? {
derivations.push((source_type.clone(), val));
Expand All @@ -372,38 +392,74 @@ impl OptionParser {
}
for (source_type, source) in self.sources.iter().rev() {
if let Some(value) = getter(source, id)? {
return Ok(OptionValue {
return Ok(OptionalOptionValue {
derivation,
source: source_type.clone(),
value,
value: Some(value),
});
}
}
Ok(OptionValue {
Ok(OptionalOptionValue {
derivation,
source: Source::Default,
value: default.to_owned(),
value: default.map(|x| x.to_owned()),
})
}

pub fn parse_bool_optional(
&self,
id: &OptionId,
default: Option<bool>,
) -> Result<OptionalOptionValue<bool>, String> {
self.parse_scalar(id, default.as_ref(), |source, id| source.get_bool(id))
}

pub fn parse_int_optional(
&self,
id: &OptionId,
default: Option<i64>,
) -> Result<OptionalOptionValue<i64>, String> {
self.parse_scalar(id, default.as_ref(), |source, id| source.get_int(id))
}

pub fn parse_float_optional(
&self,
id: &OptionId,
default: Option<f64>,
) -> Result<OptionalOptionValue<f64>, String> {
self.parse_scalar(id, default.as_ref(), |source, id| source.get_float(id))
}

pub fn parse_string_optional(
&self,
id: &OptionId,
default: Option<&str>,
) -> Result<OptionalOptionValue<String>, String> {
self.parse_scalar(id, default, |source, id| source.get_string(id))
}

pub fn parse_bool(&self, id: &OptionId, default: bool) -> Result<OptionValue<bool>, String> {
self.parse_scalar(id, &default, |source, id| source.get_bool(id))
self.parse_bool_optional(id, Some(default))
.map(OptionalOptionValue::unwrap)
}

pub fn parse_int(&self, id: &OptionId, default: i64) -> Result<OptionValue<i64>, String> {
self.parse_scalar(id, &default, |source, id| source.get_int(id))
self.parse_int_optional(id, Some(default))
.map(OptionalOptionValue::unwrap)
}

pub fn parse_float(&self, id: &OptionId, default: f64) -> Result<OptionValue<f64>, String> {
self.parse_scalar(id, &default, |source, id| source.get_float(id))
self.parse_float_optional(id, Some(default))
.map(OptionalOptionValue::unwrap)
}

pub fn parse_string(
&self,
id: &OptionId,
default: &str,
) -> Result<OptionValue<String>, String> {
self.parse_scalar(id, default, |source, id| source.get_string(id))
self.parse_string_optional(id, Some(default))
.map(OptionalOptionValue::unwrap)
}

#[allow(clippy::type_complexity)]
Expand Down

0 comments on commit 5374772

Please sign in to comment.