diff --git a/src/config/error.rs b/src/config/error.rs index 12e0140..a491638 100644 --- a/src/config/error.rs +++ b/src/config/error.rs @@ -11,10 +11,26 @@ pub enum Error { Syntax(sexp::Error), NotFound(PathBuf), InvalidUtf8, - InvalidSexp, + TypeError{expected:LispType, got: LispType}, + BadLength, Unknown, } +impl Error +{ + /// Helper function for type errors + #[inline] + pub fn bad_type(expected: T, got: U) -> Self + where T: Into, + U: Into + { + Self::TypeError { + expected: expected.into(), + got: got.into() + } + } +} + impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> @@ -33,9 +49,10 @@ impl std::fmt::Display for Error match self { Self::IO(io) => write!(f, "i/o error: {}", io), Self::Syntax(sy) => write!(f, "s-expression syntax error: {}", sy), - Self::NotFound(path) => write!(f, "File not found: {:?}", path), + Self::NotFound(path) => write!(f, "File not found: {:?}", path), Self::InvalidUtf8 => write!(f, "Invalid UTF8 decoded string or some such"), - Self::InvalidSexp => write!(f, "Invalid s-expression"), + Self::TypeError{expected, got} => write!(f, "Type mismatch: Expected {}, got {}", expected, got), + Self::BadLength => write!(f, "expected at least 1 element"), _ => write!(f, "unknown error") } } @@ -67,7 +84,7 @@ impl From> for Error impl From for Error { - fn from(from: std::str::Utf8Error) -> Self + fn from(_: std::str::Utf8Error) -> Self { Self::InvalidUtf8 } diff --git a/src/config/format.rs b/src/config/format.rs new file mode 100644 index 0000000..0188c2b --- /dev/null +++ b/src/config/format.rs @@ -0,0 +1,177 @@ +//! Formatting specifiers for the config file format +use super::*; +use sexp::{ + Sexp, + Atom, +}; + +/// A valid type in the config file +#[derive(Debug)] +pub enum LispType +{ + List, + String, + Integer, + Float, + Atom, +} + +impl From for LispType +{ + fn from(from: Sexp) -> Self + { + match from { + Sexp::Atom(atom) => atom.into(), + Sexp::List(_) => Self::List + } + } +} + +impl From for LispType +{ + fn from(from: Atom) -> Self + { + match from { + Atom::F(_) => Self::Float, + Atom::I(_) => Self::Integer, + Atom::S(_) => Self::String, + } + } +} + +impl From> for LispType +where T: Into +{ + fn from(from: Result) -> Self + { + match from { + Ok(value) => value.into(), + Err(Error::TypeError{got,..}) => got, + _ => unreachable!("you shouldn't use this here"), + } + } +} + +impl std::fmt::Display for LispType +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + f.write_str(match self { + Self::List => "list", + Self::String => "string", + Self::Integer => "integer", + Self::Float => "float", + Self::Atom => "atom", + }) + } +} + +pub trait LispExt +{ + /// Try to get `car` and `cdr` + fn try_split<'a>(&'a self) -> Result<(&'a Sexp, &'a [Sexp]), Error>; +} + +impl LispExt for Vec +{ + fn try_split<'a>(&'a self) -> Result<(&'a Sexp, &'a [Sexp]), Error> + { + if self.len() > 1 { + Ok((&self[0], &self[1..])) + } else if self.len() > 0{ + Ok((&self[0], &[])) + } else { + Err(Error::BadLength) + } + } +} + +// Sexp doesn't use try_from(), so... +pub trait TryIntoExt: Sized +{ + fn try_into_list(self) -> Result, Error>; + fn try_into_atom(self) -> Result; + + fn try_into_string(self) -> Result; + fn try_into_int(self) -> Result; + fn try_into_float(self) -> Result; +} + +impl TryIntoExt for Sexp +{ + fn try_into_list(self) -> Result, Error> + { + match self { + Sexp::List(this) => Ok(this), + other => Err(Error::bad_type(LispType::List, other)), + } + } + fn try_into_atom(self) -> Result + { + match self { + Sexp::Atom(this) => Ok(this), + other => Err(Error::bad_type(LispType::Atom, other)), + } + } + + fn try_into_string(self) -> Result + { + match self.try_into_atom() { + Ok(Atom::S(this)) => Ok(this), + other => Err(Error::bad_type(LispType::String, other)), + } + } + + fn try_into_int(self) -> Result + { + match self.try_into_atom() { + Ok(Atom::I(this)) => Ok(this), + other => Err(Error::bad_type(LispType::Integer, other)), + } + } + fn try_into_float(self) -> Result + { + match self.try_into_atom() { + Ok(Atom::F(this)) => Ok(this), + other => Err(Error::bad_type(LispType::Float, other)), + } + } +} + +impl TryIntoExt for Atom +{ + #[inline] + fn try_into_list(self) -> Result, Error> + { + Err(Error::bad_type(LispType::List, self)) + } + #[inline] + fn try_into_atom(self) -> Result + { + Ok(self) + } + + fn try_into_string(self) -> Result + { + match self { + Atom::S(this) => Ok(this), + other => Err(Error::bad_type(LispType::String, other)), + } + } + + fn try_into_int(self) -> Result + { + match self { + Atom::I(this) => Ok(this), + other => Err(Error::bad_type(LispType::Integer, other)), + } + } + fn try_into_float(self) -> Result + { + match self { + Atom::F(this) => Ok(this), + other => Err(Error::bad_type(LispType::Float, other)), + } + } + +} diff --git a/src/config/mod.rs b/src/config/mod.rs index d191de3..3825a70 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -9,6 +9,10 @@ use std::{ }; mod parse; + +mod format; +pub use format::*; + mod error; pub use error::*; diff --git a/src/config/parse.rs b/src/config/parse.rs index 20b64ed..8555970 100644 --- a/src/config/parse.rs +++ b/src/config/parse.rs @@ -1,10 +1,13 @@ //! Parses the config files use super::*; - -use tokio::fs::File; -use tokio::prelude::*; +use std::{ + convert::TryFrom, +}; +use tokio::{ + prelude::*, + fs::File, +}; use sexp::{ - parse, Sexp, Atom, }; @@ -13,7 +16,7 @@ fn get_atom_string<'a>(maybe_atom: &'a Sexp) -> Result<&'a String, Error> { match &maybe_atom { Sexp::Atom(Atom::S(string)) => Ok(string), - _ => Err(Error::InvalidSexp), + &opt => Err(Error::bad_type(LispType::String, opt.to_owned())), } } @@ -44,6 +47,7 @@ fn set_debug(to: &mut Config, cdr: &[Sexp]) -> Result<(), Error> { #[inline] fn mwee(to: &mut Config, cdr: &[Sexp]) -> Result<(), Error> { println!("{:?}", cdr); + //todo!() Ok(()) } @@ -54,44 +58,35 @@ pub async fn global(to: &mut Config, path: impl AsRef) -> Result<(), error if path.exists() { let mut file = File::open(path).await?; - let mut contents = vec![]; + let mut contents = if let Ok(Ok(wv)) = file.metadata().await + .map(|x| x.len()) + .map(|x| usize::try_from(x)) { + Vec::with_capacity(wv) + } else { + Vec::new() + }; file.read_to_end(&mut contents).await?; let parsed = sexp::parse(std::str::from_utf8(&contents[..])?)?; - if let Sexp::List(sexp) = parsed { - for sexp in sexp.into_iter() { - if let Sexp::List(sexp) = sexp { - - let car = &sexp[0]; - let cdr = &sexp[1..]; - match car { - Sexp::List(_) => return Err(Error::InvalidSexp), - Sexp::Atom(car) => { - if let Atom::S(car) = car { - match car.to_lowercase().trim() { - "jobs-dir" => add_job(to, cdr), - "debug" => set_debug(to ,cdr), - "allow" => mwee(to, cdr), - "deny" => mwee(to, cdr), - _ => return Err(Error::Unknown), - }?; - } else { - return Err(Error::InvalidSexp); - } - } - } - } - else { - return Err(Error::InvalidSexp); - } - } - } else { - return Err(Error::InvalidSexp); + let sexp = parsed.try_into_list()?; + for sexp in sexp.into_iter() { + let sexp = sexp.try_into_list()?; + + let (car,cdr) = sexp.try_split()?; + let car = car.clone()/* <- i'm too lazy to make ref versions of these `try_into_*` functions, so this will do*/.try_into_string()?; + match car.to_lowercase().trim() { + "jobs-dir" => add_job(to, cdr), + "debug" => set_debug(to ,cdr), + "allow" => mwee(to, cdr), + "deny" => mwee(to, cdr), + _ => return Err(Error::Unknown), + }?; } - - Ok(()) + Ok(()) + + } else { - return Err(Error::NotFound(path.to_owned())); + Err(Error::NotFound(path.to_owned())) } } diff --git a/src/main.rs b/src/main.rs index 9a53d13..2ddd9c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,7 +62,7 @@ async fn main() -> Result<(), Box> { debug!("Initialised"); - println!("{:?}", config::parse_global_single("/home/manx/flon.txt").await.expect("Waaaaaah")); + println!("{:?}", config::parse_global_single("example.rori").await.expect("Waaaaaah")); //let (mut tx, h) = do_thing_every().await?;