use std::collections::HashMap;
use std::fs;
use std::io::{self, BufRead, Read};
use std::path::Path;
use anyhow::Result;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Tristate {
True,
False,
Module,
NotSet,
}
#[derive(Clone, Debug)]
pub enum Value {
Tristate(Tristate),
String(String),
}
impl Value {
pub fn to_rustc_cfg(&self, prefix: impl AsRef<str>, key: impl AsRef<str>) -> Option<String> {
match self {
Value::Tristate(Tristate::True) => Some(""),
Value::String(s) => Some(s.as_str()),
_ => None,
}
.map(|value| {
if value.is_empty() {
format!(
"{}_{}",
prefix.as_ref().to_lowercase(),
key.as_ref().to_lowercase()
)
} else {
format!(
"{}_{}=\"{}\"",
prefix.as_ref().to_lowercase(),
key.as_ref().to_lowercase(),
value.replace('\"', "\\\"")
)
}
})
}
}
pub fn try_from_json_file(path: impl AsRef<Path>) -> Result<impl Iterator<Item = (String, Value)>> {
try_from_json(fs::File::open(path)?)
}
pub fn try_from_json<R>(reader: R) -> Result<impl Iterator<Item = (String, Value)>>
where
R: Read,
{
let values: HashMap<String, serde_json::Value> = serde_json::from_reader(reader)?;
let iter = values.into_iter().filter_map(|(k, v)| match v {
serde_json::Value::Bool(true) => Some((k, Value::Tristate(Tristate::True))),
serde_json::Value::Bool(false) => Some((k, Value::Tristate(Tristate::False))),
serde_json::Value::String(value) => Some((k, Value::String(value))),
_ => None,
});
Ok(iter)
}
pub fn try_from_config_file(
path: impl AsRef<Path>,
) -> Result<impl Iterator<Item = (String, Value)>> {
try_from_config(fs::File::open(path.as_ref())?)
}
pub fn try_from_config<R>(reader: R) -> Result<impl Iterator<Item = (String, Value)>>
where
R: Read,
{
let iter = io::BufReader::new(reader)
.lines()
.filter_map(|line| line.ok().map(|l| l.trim().to_owned()))
.filter(|line| !line.starts_with('#'))
.filter_map(|line| {
let mut split = line.split('=');
if let Some(key) = split.next() {
split
.next()
.map(|v| v.trim())
.and_then(parse_config_value)
.map(|value| (key.to_owned(), value))
} else {
None
}
});
Ok(iter)
}
fn parse_config_value(str: impl AsRef<str>) -> Option<Value> {
let str = str.as_ref();
Some(if str.starts_with('\"') {
Value::String(str[1..str.len() - 1].to_owned())
} else if str == "y" {
Value::Tristate(Tristate::True)
} else if str == "n" {
Value::Tristate(Tristate::False)
} else if str == "m" {
Value::Tristate(Tristate::Module)
} else {
return None;
})
}