/* 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" }) } }