use std::fmt::{self, Display, Formatter};
#[derive(Debug)]
pub enum Error {
AtomToUint,
AtomToStr,
ExpectedNull,
ImplType,
MissingValue,
UnexpectedAtom,
UnexpectedCell,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Self::AtomToUint => write!(
f,
"the atom is too large to fit in the unsigned integer type"
),
Self::AtomToStr => write!(f, "the atom is not composed of valid UTF-8 bytes"),
Self::ExpectedNull => write!(f, "a null atom was expected"),
Self::ImplType => write!(f, "an error specific to the implementing type occurred"),
Self::MissingValue => write!(f, "the noun does not have a value at this axis"),
Self::UnexpectedAtom => write!(f, "an atom was encountered when a cell was expected"),
Self::UnexpectedCell => write!(f, "a cell was encountered when an atom was expected"),
}
}
}
#[macro_export]
macro_rules! convert {
($noun:expr => Vec<$elem_type:ty>) => {{
use $crate::{convert::Error, noun::Noun};
let mut noun = $noun;
let mut elems: Vec<$elem_type> = Vec::new();
loop {
match noun {
Noun::Atom(atom) => {
if atom.is_null() {
break Ok(elems);
} else {
break Err(Error::ExpectedNull);
}
}
Noun::Cell(cell) => match <$elem_type>::try_from(cell.head_ref()) {
Ok(elem) => {
elems.push(elem);
noun = cell.tail_ref();
}
Err(err) => break Err(err),
},
}
}
}};
($noun:expr => HashMap<$key_type:ty, $val_type:ty>) => {{
use std::collections::HashMap;
use $crate::{convert::Error, noun::Noun};
let mut noun = $noun;
let mut map: HashMap<$key_type, $val_type> = HashMap::new();
loop {
match noun {
Noun::Atom(atom) => {
if atom.is_null() {
break Ok(map);
} else {
break Err(Error::ExpectedNull);
}
}
Noun::Cell(cell) => {
if let Noun::Cell(head) = cell.head_ref() {
match (
<$key_type>::try_from(head.head_ref()),
<$val_type>::try_from(head.tail_ref()),
) {
(Ok(key), Ok(val)) => {
map.insert(key, val);
noun = cell.tail_ref();
}
(Err(err), _) => break Err(err),
(_, Err(err)) => break Err(err),
}
} else {
break Err(Error::UnexpectedAtom);
}
}
}
}
}};
($iter:expr => Noun) => {{
use $crate::{cell::Cell, noun::Noun, Rc};
let mut noun = Rc::<Noun>::from(Noun::null());
let mut iter = $iter.rev();
loop {
match iter.next() {
Some(elem) => match Noun::try_from(elem) {
Ok(elem) => {
noun = Rc::<Noun>::from(Noun::from(Cell::from([
Rc::<Noun>::from(elem),
noun,
])));
}
Err(err) => break Err(err),
},
None => break Ok(Rc::try_unwrap(noun).unwrap()),
}
}
}};
}
#[cfg(test)]
mod tests {
use crate::{atom::Atom, cell::Cell, noun::Noun};
#[test]
fn convert() {
{
{
let noun = Noun::from(Cell::from(["no", "null", "terminator"]));
assert!(convert!(&noun => Vec<String>).is_err());
}
{
let noun = Noun::from(Cell::from([
Noun::from(Cell::from(["unexpected", "cell"])),
Noun::null(),
]));
assert!(convert!(&noun => Vec<String>).is_err());
}
}
{
{
let strings = ["a", "b", "c"];
let noun = convert!(strings.iter() => Noun).expect("&[str] to Noun");
assert_eq!(
noun,
Noun::from(Cell::from([
Atom::from("a"),
Atom::from("b"),
Atom::from("c"),
Atom::null()
]))
);
}
}
}
}