//! 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, /// A non-descript atom, either String, Integer, Float, or the empty list. Atom, } impl From for LispType { fn from(from: Sexp) -> Self { match from { Sexp::Atom(atom) => atom.into(), Sexp::List(_) => Self::List } } } impl From<&Sexp> for LispType { fn from(from: &Sexp) -> Self { match from { Sexp::Atom(atom) => atom.into(), Sexp::List(_) => Self::List } } } impl From<&Atom> 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 { 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 => "nil", }) } } 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::UnexpectedEmpty) } } } // 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)), } } } // To avoid some `clone()`s pub trait TryGetExt: Sized { fn try_get_list(&self) -> Result<&[Sexp], Error>; fn try_get_atom(&self) -> Result<&Atom, Error>; fn try_get_string(&self) -> Result<&String, Error>; fn try_get_int(&self) -> Result<&i64, Error>; fn try_get_float(&self) -> Result<&f64, Error>; } impl TryGetExt for Sexp { fn try_get_list(&self) -> Result<&[Sexp], Error> { match self { Self::List(list) => Ok(&list[..]), other => Err(Error::bad_type(LispType::List, other)), } } fn try_get_atom(&self) -> Result<&Atom, Error> { match self { Self::Atom(atom) => Ok(atom), other => Err(Error::bad_type(LispType::Atom, other)), } } fn try_get_string(&self) -> Result<&String, Error> { match self { Self::Atom(Atom::S(string)) => Ok(string), other => Err(Error::bad_type(LispType::String, other)), } } fn try_get_int(&self) -> Result<&i64, Error> { match self { Self::Atom(Atom::I(int)) => Ok(int), other => Err(Error::bad_type(LispType::Integer, other)), } } fn try_get_float(&self) -> Result<&f64, Error> { match self { Self::Atom(Atom::F(float)) => Ok(float), other => Err(Error::bad_type(LispType::Float, other)), } } }