/*
Copyright 2021, 2023-2024 Gabriel Bjørnager Jen-
sen.
This file is part of benoit-cli.
benoit-cli is free software: you can redistrib-
ute it and/or modify it under the terms of the
GNU General Public License as published by the
Free Software Foundation, either version 3 of
the License, or (at your option) any later ver-
sion.
benoit-cli is distributed in the hope that it
will be useful, but WITHOUT ANY WARRANTY; with-
out even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Gene-
ral Public License along with benoit-cli. If
not, see .
*/
use crate::config::{Field, Section};
use crate::error::Error;
use benoit::PRECISION;
use benoit::complex::Complex;
use rug::Float;
use std::borrow::Cow;
use std::fs::canonicalize;
use std::path::PathBuf;
use std::str::FromStr;
use toml::Value;
/// Used for loading configuration fields.
///
/// More specifically, this is to be used with the untyped [`Field`] type, where using [`take`](Take::take) collapses the field into a definitively typed object.
pub trait Take {
/// Collapses the field into the given type `T`.
///
/// # Errors
///
/// Returns an error if the field doesn't exist or is a different type.
fn take(self) -> Result;
}
fn test_field_domain(name: &str, value: &T, min: U, max: U) -> Result<(), Error>
where
T: PartialOrd + ToString,
U: Copy + Into + ToString, {
if *value < min.into() {
Err(Error::FieldLowerBounds {
name: name.to_owned(),
value: value.to_string(),
limit: min.to_string(),
})
} else if *value > max.into() {
Err(Error::FieldUpperBounds {
name: name.to_owned(),
value: value.to_string(),
limit: max.to_string(),
})
} else {
Ok(())
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
let Value::Boolean(value) = *self.borrow_value()? else {
return Err(Error::WrongFieldType { name: self.name, ok_type: "bool" });
};
Ok(value)
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
let s = if let Value::String(ref s) = *self.borrow_value()? {
Ok(s)
} else {
Err(Error::WrongFieldType { name: self.name, ok_type: "complex" })
}?;
FromStr::from_str(s).map_err(Into::into)
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Float(value) = *self.borrow_value()? {
// Is this even possible?
if value.is_nan() {
return Err(Error::NonNumberField { name: self.name });
}
if value.is_infinite() {
return Err(Error::InfiniteField { name: self.name });
}
test_field_domain(&self.name, &value, -f32::MAX, f32::MAX)?;
Ok(value as f32)
} else {
Err(Error::WrongFieldType { name: self.name, ok_type: "float" })
}
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Float(value) = *self.borrow_value()? {
// Is this even possible?
if value.is_nan() {
return Err(Error::NonNumberField { name: self.name });
}
if value.is_infinite() {
return Err(Error::InfiniteField { name: self.name });
}
Ok(value)
} else {
Err(Error::WrongFieldType { name: self.name, ok_type: "float" })
}
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::String(ref value) = *self.borrow_value()? {
if let Ok(value) = Float::parse(value) {
return Ok(Float::with_val(PRECISION, value));
}
}
Err(Error::WrongFieldType { name: self.name, ok_type: "bigfloat" })
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Integer(value) = *self.borrow_value()? {
test_field_domain(&self.name, &value, i16::MIN, i16::MAX)?;
return Ok(TryFrom::try_from(value).unwrap());
};
Err(Error::WrongFieldType { name: self.name, ok_type: "integer" })
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Integer(value) = *self.borrow_value()? {
test_field_domain(&self.name, &value, i32::MIN, i32::MAX)?;
return Ok(TryFrom::try_from(value).unwrap());
};
Err(Error::WrongFieldType { name: self.name, ok_type: "integer" })
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Integer(value) = *self.borrow_value()? {
return Ok(value);
};
Err(Error::WrongFieldType { name: self.name, ok_type: "integer" })
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Integer(value) = *self.borrow_value()? {
test_field_domain(&self.name, &value, i8::MIN, i8::MAX)?;
return Ok(TryFrom::try_from(value).unwrap());
};
Err(Error::WrongFieldType { name: self.name, ok_type: "integer" })
}
}
impl Take for Field<'_> {
fn take(self) -> Result {
let s = >::take(self)?;
canonicalize(&s).map_err(|e| Error::InvalidPath { path: s, source: e })
}
}
impl<'a> Take> for Field<'a> {
fn take(mut self) -> Result, Error> {
if let Value::Table(ref table) = *self.borrow_value()? {
return Ok(Section {
name: Some(self.name),
table: Cow::Borrowed(table),
});
}
Err(Error::WrongFieldType { name: self.name, ok_type: "section" })
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::String(ref s) = *self.borrow_value()? {
Ok(s.to_owned())
} else {
Err(Error::WrongFieldType { name: self.name, ok_type: "string" })
}
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Integer(value) = *self.borrow_value()? {
if value.is_negative() {
return Err(Error::NegativeField { name: self.name });
}
test_field_domain(&self.name, &value, u16::MIN, u16::MAX)?;
return Ok(TryFrom::try_from(value).unwrap());
};
Err(Error::WrongFieldType { name: self.name, ok_type: "integer" })
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Integer(value) = *self.borrow_value()? {
if value.is_negative() {
return Err(Error::NegativeField { name: self.name });
}
test_field_domain(&self.name, &value, u32::MIN, u32::MAX)?;
return Ok(TryFrom::try_from(value).unwrap());
};
Err(Error::WrongFieldType { name: self.name, ok_type: "integer" })
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Integer(value) = *self.borrow_value()? {
if value.is_negative() {
return Err(Error::NegativeField { name: self.name })
}
return Ok(TryFrom::try_from(value).unwrap());
};
Err(Error::WrongFieldType { name: self.name, ok_type: "integer" })
}
}
impl Take for Field<'_> {
fn take(mut self) -> Result {
if let Value::Integer(value) = *self.borrow_value()? {
if value.is_negative() {
return Err(Error::NegativeField { name: self.name });
}
test_field_domain(&self.name, &value, u8::MIN, u8::MAX)?;
return Ok(TryFrom::try_from(value).unwrap());
};
Err(Error::WrongFieldType { name: self.name, ok_type: "integer" })
}
}