config: change default config to use toml-based configuration
This commit is contained in:
parent
e24a61bc62
commit
3cebf651c5
58 changed files with 14093 additions and 145 deletions
281
toml-config/src/config/extractor.rs
Normal file
281
toml-config/src/config/extractor.rs
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
use {
|
||||
crate::{
|
||||
config::context::Context,
|
||||
toml::{
|
||||
toml_span::{Span, Spanned, SpannedExt},
|
||||
toml_value::Value,
|
||||
},
|
||||
},
|
||||
ahash::AHashSet,
|
||||
error_reporter::Report,
|
||||
indexmap::IndexMap,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct Extractor<'v> {
|
||||
cx: &'v Context<'v>,
|
||||
table: &'v IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
used: Vec<&'static str>,
|
||||
log_unused: bool,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<'v> Extractor<'v> {
|
||||
pub fn new(
|
||||
cx: &'v Context<'v>,
|
||||
span: Span,
|
||||
table: &'v IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cx,
|
||||
table,
|
||||
used: Default::default(),
|
||||
log_unused: true,
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&mut self, name: &'static str) -> Option<&'v Spanned<Value>> {
|
||||
let v = self.table.get(name);
|
||||
if v.is_some() {
|
||||
self.used.push(name);
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
pub fn ignore_unused(&mut self) {
|
||||
self.log_unused = false;
|
||||
}
|
||||
|
||||
pub fn extract<E: Extractable<'v>, U>(&mut self, e: E) -> Result<E::Output, Spanned<U>>
|
||||
where
|
||||
ExtractorError: Into<U>,
|
||||
{
|
||||
e.extract(self).map_err(|e| e.map(|e| e.into()))
|
||||
}
|
||||
|
||||
pub fn extract_or_ignore<E: Extractable<'v>, U>(
|
||||
&mut self,
|
||||
e: E,
|
||||
) -> Result<E::Output, Spanned<U>>
|
||||
where
|
||||
ExtractorError: Into<U>,
|
||||
{
|
||||
let res = self.extract(e);
|
||||
if res.is_err() {
|
||||
self.ignore_unused();
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Drop for Extractor<'v> {
|
||||
fn drop(&mut self) {
|
||||
if !self.log_unused {
|
||||
return;
|
||||
}
|
||||
if self.used.len() == self.table.len() {
|
||||
return;
|
||||
}
|
||||
let used: AHashSet<_> = self.used.iter().copied().collect();
|
||||
for key in self.table.keys() {
|
||||
if !used.contains(key.value.as_str()) {
|
||||
#[derive(Debug, Error)]
|
||||
#[error("Ignoring unknown key {0}")]
|
||||
struct Err<'a>(&'a str);
|
||||
let err = self.cx.error2(key.span, Err(&key.value));
|
||||
log::warn!("{}", Report::new(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Eq, PartialEq)]
|
||||
pub enum ExtractorError {
|
||||
#[error("Missing field {0}")]
|
||||
MissingField(&'static str),
|
||||
#[error("Expected {0} but found {1}")]
|
||||
Expected(&'static str, &'static str),
|
||||
#[error("Value must fit in a u32")]
|
||||
U32,
|
||||
#[error("Value must fit in a u64")]
|
||||
U64,
|
||||
#[error("Value must fit in a i32")]
|
||||
I32,
|
||||
}
|
||||
|
||||
pub trait Extractable<'v> {
|
||||
type Output;
|
||||
|
||||
fn extract(
|
||||
self,
|
||||
extractor: &mut Extractor<'v>,
|
||||
) -> Result<Self::Output, Spanned<ExtractorError>>;
|
||||
}
|
||||
|
||||
impl<'v, T, F> Extractable<'v> for F
|
||||
where
|
||||
F: FnOnce(&mut Extractor<'v>) -> Result<T, Spanned<ExtractorError>>,
|
||||
{
|
||||
type Output = T;
|
||||
|
||||
fn extract(
|
||||
self,
|
||||
extractor: &mut Extractor<'v>,
|
||||
) -> Result<Self::Output, Spanned<ExtractorError>> {
|
||||
self(extractor)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn val(
|
||||
name: &'static str,
|
||||
) -> impl for<'v> FnOnce(&mut Extractor<'v>) -> Result<Spanned<&'v Value>, Spanned<ExtractorError>>
|
||||
{
|
||||
move |extractor: &mut Extractor| match extractor.get(name) {
|
||||
None => Err(ExtractorError::MissingField(name).spanned(extractor.span)),
|
||||
Some(v) => Ok(v.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! ty {
|
||||
($f:ident, $lt:lifetime, $ty:ident, $ret:ty, $v:ident, $map:expr, $name:expr) => {
|
||||
#[allow(dead_code)]
|
||||
pub fn $f(
|
||||
name: &'static str,
|
||||
) -> impl for<$lt> FnOnce(&mut Extractor<$lt>) -> Result<Spanned<$ret>, Spanned<ExtractorError>> {
|
||||
move |extractor: &mut Extractor| {
|
||||
val(name)(extractor).and_then(|v| match v.value {
|
||||
Value::$ty($v) => Ok($map.spanned(v.span)),
|
||||
_ => Err(ExtractorError::Expected($name, v.value.name()).spanned(v.span)),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ty!(str, 'a, String, &'a str, v, v.as_str(), "a string");
|
||||
ty!(int, 'a, Integer, i64, v, *v, "an integer");
|
||||
ty!(flt, 'a, Float, f64, v, *v, "a float");
|
||||
ty!(bol, 'a, Boolean, bool, v, *v, "a boolean");
|
||||
ty!(arr, 'a, Array, &'a [Spanned<Value>], v, &**v, "an array");
|
||||
ty!(tbl, 'a, Table, &'a IndexMap<Spanned<String>, Spanned<Value>>, v, v, "a table");
|
||||
|
||||
pub fn fltorint(
|
||||
name: &'static str,
|
||||
) -> impl for<'a> FnOnce(&mut Extractor<'a>) -> Result<Spanned<f64>, Spanned<ExtractorError>> {
|
||||
move |extractor: &mut Extractor| {
|
||||
val(name)(extractor).and_then(|v| match *v.value {
|
||||
Value::Float(f) => Ok(f.spanned(v.span)),
|
||||
Value::Integer(i) => Ok((i as f64).spanned(v.span)),
|
||||
_ => Err(
|
||||
ExtractorError::Expected("a float or an integer", v.value.name()).spanned(v.span),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! int {
|
||||
($f:ident, $ty:ident, $err:ident) => {
|
||||
#[allow(dead_code)]
|
||||
pub fn $f(
|
||||
name: &'static str,
|
||||
) -> impl for<'v> FnOnce(&mut Extractor<'v>) -> Result<Spanned<$ty>, Spanned<ExtractorError>> {
|
||||
move |extractor: &mut Extractor| {
|
||||
int(name)(extractor).and_then(|v| {
|
||||
v.value
|
||||
.try_into()
|
||||
.map(|n: $ty| n.spanned(v.span))
|
||||
.map_err(|_| ExtractorError::$err.spanned(v.span))
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int!(n32, u32, U32);
|
||||
int!(n64, u64, U64);
|
||||
int!(s32, i32, I32);
|
||||
|
||||
pub fn recover<F>(f: F) -> Recover<F> {
|
||||
Recover(f)
|
||||
}
|
||||
|
||||
pub struct Recover<E>(E);
|
||||
|
||||
impl<'v, E> Extractable<'v> for Recover<E>
|
||||
where
|
||||
E: Extractable<'v>,
|
||||
<E as Extractable<'v>>::Output: Default,
|
||||
{
|
||||
type Output = <E as Extractable<'v>>::Output;
|
||||
|
||||
fn extract(
|
||||
self,
|
||||
extractor: &mut Extractor<'v>,
|
||||
) -> Result<Self::Output, Spanned<ExtractorError>> {
|
||||
match self.0.extract(extractor) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => {
|
||||
log::warn!("{}", extractor.cx.error(e));
|
||||
Ok(<E as Extractable<'v>>::Output::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn opt<F>(f: F) -> Opt<F> {
|
||||
Opt(f)
|
||||
}
|
||||
|
||||
pub struct Opt<E>(E);
|
||||
|
||||
impl<'v, E> Extractable<'v> for Opt<E>
|
||||
where
|
||||
E: Extractable<'v>,
|
||||
{
|
||||
type Output = Option<<E as Extractable<'v>>::Output>;
|
||||
|
||||
fn extract(
|
||||
self,
|
||||
extractor: &mut Extractor<'v>,
|
||||
) -> Result<Self::Output, Spanned<ExtractorError>> {
|
||||
match self.0.extract(extractor) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(e) if matches!(e.value, ExtractorError::MissingField(_)) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuples {
|
||||
($($idx:tt: $name:ident,)*) => {
|
||||
impl<'v, $($name,)*> Extractable<'v> for ($($name,)*)
|
||||
where $($name: Extractable<'v>,)*
|
||||
{
|
||||
type Output = ($($name::Output,)*);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn extract(self, extractor: &mut Extractor<'v>) -> Result<Self::Output, Spanned<ExtractorError>> {
|
||||
$(
|
||||
let $name = self.$idx.extract(extractor);
|
||||
)*
|
||||
Ok((
|
||||
$(
|
||||
$name?,
|
||||
)*
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tuples!(0:T0,);
|
||||
tuples!(0:T0,1:T1,);
|
||||
tuples!(0:T0,1:T1,2:T2,);
|
||||
tuples!(0:T0,1:T1,2:T2,3:T3,);
|
||||
tuples!(0:T0,1:T1,2:T2,3:T3,4:T4,);
|
||||
tuples!(0:T0,1:T1,2:T2,3:T3,4:T4,5:T5,);
|
||||
tuples!(0:T0,1:T1,2:T2,3:T3,4:T4,5:T5,6:T6,);
|
||||
tuples!(0:T0,1:T1,2:T2,3:T3,4:T4,5:T5,6:T6,7:T7,);
|
||||
tuples!(0:T0,1:T1,2:T2,3:T3,4:T4,5:T5,6:T6,7:T7,8:T8,);
|
||||
tuples!(0:T0,1:T1,2:T2,3:T3,4:T4,5:T5,6:T6,7:T7,8:T8,9:T9,);
|
||||
Loading…
Add table
Add a link
Reference in a new issue