mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 09:17:30 +03:00
configparser: add type conversions
Summary: This is to match APIs like `ui.configbool` in the Python land. The new APIs use string types to make them easier to use. Reviewed By: xavierd Differential Revision: D17423798 fbshipit-source-id: 747b094c4cc6afef2e818a1862e820647328c4c2
This commit is contained in:
parent
239651f2fa
commit
5dd6fe1be3
@ -12,6 +12,10 @@ use failure::Fail;
|
||||
/// The error type for parsing config files.
|
||||
#[derive(Fail, Debug)]
|
||||
pub enum Error {
|
||||
/// Unable to convert to a type.
|
||||
#[fail(display = "{}", _0)]
|
||||
Convert(String),
|
||||
|
||||
/// Unable to parse a file due to syntax.
|
||||
#[fail(display = "{:?}:\n{}", _0, _1)]
|
||||
Parse(PathBuf, String),
|
||||
|
@ -56,6 +56,31 @@ pub trait ConfigSetHgExt {
|
||||
/// Load a specified config file. Respect HGPLAIN environment variables.
|
||||
/// Return errors parsing files.
|
||||
fn load_hgrc(&mut self, path: impl AsRef<Path>, source: &'static str) -> Vec<Error>;
|
||||
|
||||
/// Get a config item. Convert to type `T`.
|
||||
///
|
||||
/// If the config item is not set, calculate it using `default_func`.
|
||||
fn get_or<T: FromConfigValue>(
|
||||
&self,
|
||||
section: &str,
|
||||
name: &str,
|
||||
default_func: impl Fn() -> T,
|
||||
) -> Fallible<T>;
|
||||
|
||||
/// Get a config item. Convert to type `T`.
|
||||
///
|
||||
/// If the config item is not set, return `T::default()`.
|
||||
fn get_or_default<T: Default + FromConfigValue>(
|
||||
&self,
|
||||
section: &str,
|
||||
name: &str,
|
||||
) -> Fallible<T> {
|
||||
self.get_or(section, name, || Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FromConfigValue: Sized {
|
||||
fn try_from_bytes(bytes: &[u8]) -> Fallible<Self>;
|
||||
}
|
||||
|
||||
/// Load system, user config files.
|
||||
@ -292,6 +317,50 @@ impl ConfigSetHgExt for ConfigSet {
|
||||
let opts = Options::new().source(source).process_hgplain();
|
||||
self.load_path(path, &opts)
|
||||
}
|
||||
|
||||
fn get_or<T: FromConfigValue>(
|
||||
&self,
|
||||
section: &str,
|
||||
name: &str,
|
||||
default_func: impl Fn() -> T,
|
||||
) -> Fallible<T> {
|
||||
match ConfigSet::get(self, section, name) {
|
||||
None => Ok(default_func()),
|
||||
Some(bytes) => Ok(T::try_from_bytes(&bytes)?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromConfigValue for bool {
|
||||
fn try_from_bytes(bytes: &[u8]) -> Fallible<Self> {
|
||||
let value = std::str::from_utf8(bytes)?.to_lowercase();
|
||||
match value.as_ref() {
|
||||
"1" | "yes" | "true" | "on" | "always" => Ok(true),
|
||||
"0" | "no" | "false" | "off" | "never" => Ok(false),
|
||||
_ => Err(Error::Convert(format!("invalid bool: {}", value)).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromConfigValue for i64 {
|
||||
fn try_from_bytes(bytes: &[u8]) -> Fallible<Self> {
|
||||
let value = std::str::from_utf8(bytes)?.parse()?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromConfigValue for String {
|
||||
fn try_from_bytes(bytes: &[u8]) -> Fallible<Self> {
|
||||
String::from_utf8(bytes.to_vec())
|
||||
.map_err(|_| Error::Convert(format!("{:?} is not utf8 encoded", bytes)).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromConfigValue> FromConfigValue for Vec<T> {
|
||||
fn try_from_bytes(bytes: &[u8]) -> Fallible<Self> {
|
||||
let items = parse_list(bytes);
|
||||
items.into_iter().map(|s| T::try_from_bytes(&s)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a configuration value as a list of comma/space separated strings.
|
||||
@ -786,4 +855,42 @@ mod tests {
|
||||
vec![b("a"), b(" c"), b("\""), b("d")]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_or() {
|
||||
let mut cfg = ConfigSet::new();
|
||||
cfg.parse(
|
||||
"[foo]\n\
|
||||
bool1 = yes\n\
|
||||
bool2 = unknown\n\
|
||||
bools = 1, TRUE, On, aLwAys, 0, false, oFF, never\n\
|
||||
int1 = -33\n\
|
||||
list1 = x y z\n\
|
||||
list3 = 2, 3, 1",
|
||||
&"test".into(),
|
||||
);
|
||||
|
||||
assert_eq!(cfg.get_or("foo", "bar", || 3).unwrap(), 3);
|
||||
assert_eq!(cfg.get_or("foo", "bool1", || false).unwrap(), true);
|
||||
assert_eq!(
|
||||
format!("{}", cfg.get_or("foo", "bool2", || true).unwrap_err()),
|
||||
"invalid bool: unknown"
|
||||
);
|
||||
assert_eq!(cfg.get_or("foo", "int1", || 42).unwrap(), -33);
|
||||
assert_eq!(
|
||||
cfg.get_or("foo", "list1", || vec!["x".to_string()])
|
||||
.unwrap(),
|
||||
vec!["x", "y", "z"]
|
||||
);
|
||||
assert_eq!(
|
||||
cfg.get_or("foo", "list3", || vec![0]).unwrap(),
|
||||
vec![2, 3, 1]
|
||||
);
|
||||
|
||||
assert_eq!(cfg.get_or_default::<bool>("foo", "bool1").unwrap(), true);
|
||||
assert_eq!(
|
||||
cfg.get_or_default::<Vec<bool>>("foo", "bools").unwrap(),
|
||||
vec![true, true, true, true, false, false, false, false]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user