hocon-rs 0.2.0

Rust implementation of HOCON
Documentation
use std::fmt::Display;

use derive_more::Constructor;

use crate::{
    join,
    path::{Key, Path},
};

#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Constructor)]
pub(crate) struct RefPath<'a> {
    pub first: RefKey<'a>,
    pub remainder: Option<Box<RefPath<'a>>>,
}

impl<'a> RefPath<'a> {
    pub fn from_slice(paths: &'a [&'a str]) -> crate::Result<RefPath<'a>> {
        let mut dummy = RefPath::new(RefKey::Str(""), None);
        let mut curr = &mut dummy;
        for p in paths {
            curr.remainder = Some(RefPath::new(RefKey::Str(p), None).into());
            curr = curr.remainder.as_mut().unwrap();
        }
        match dummy.remainder {
            Some(path) => Ok(*path),
            None => Err(crate::error::Error::InvalidPathExpression("path is empty")),
        }
    }

    pub fn next(&self) -> Option<&RefPath<'a>> {
        self.remainder.as_deref()
    }

    pub fn join(&self, path: RefPath<'a>) -> RefPath<'a> {
        let mut cloned = self.clone();
        let tail = cloned.tail_mut();
        tail.remainder = Some(Box::new(path));
        cloned
    }

    pub fn tail_mut(&mut self) -> &mut RefPath<'a> {
        let mut tail = self;
        while tail.remainder.is_some() {
            tail = tail.remainder.as_mut().unwrap();
        }
        tail
    }

    pub fn from(path: &Path) -> RefPath<'_> {
        let mut dummy = RefPath::new(RefKey::Str(""), None);
        let mut tail = &mut dummy;
        for ele in path.iter() {
            let p = RefPath::new(RefKey::from_owned(&ele.first), None);
            tail.remainder = Some(Box::new(p));
            tail = tail.remainder.as_mut().unwrap();
        }
        *dummy.remainder.unwrap()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum RefKey<'a> {
    Str(&'a str),
    Index(usize),
}

impl<'a> RefKey<'a> {
    pub(crate) fn to_owned(&self) -> Key {
        match self {
            RefKey::Str(s) => Key::String(s.to_string()),
            RefKey::Index(i) => Key::Index(*i),
        }
    }

    pub(crate) fn from_owned(key: &Key) -> RefKey<'_> {
        match key {
            Key::String(s) => RefKey::Str(s),
            Key::Index(i) => RefKey::Index(*i),
        }
    }
}

impl<'a> Display for RefKey<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RefKey::Str(s) => write!(f, "{s}"),
            RefKey::Index(i) => write!(f, "{i}"),
        }
    }
}

impl<'a> PartialEq<Key> for RefKey<'a> {
    fn eq(&self, other: &Key) -> bool {
        match (self, other) {
            (RefKey::Str(a), Key::String(b)) => a == b,
            (RefKey::Str(_), Key::Index(_)) | (RefKey::Index(_), Key::String(_)) => false,
            (RefKey::Index(a), Key::Index(b)) => a == b,
        }
    }
}

impl<'a> PartialEq<RefKey<'a>> for Key {
    fn eq(&self, other: &RefKey<'a>) -> bool {
        match (self, other) {
            (Key::String(a), RefKey::Str(b)) => a == b,
            (Key::String(_), RefKey::Index(_)) | (Key::Index(_), RefKey::Str(_)) => false,
            (Key::Index(a), RefKey::Index(b)) => a == b,
        }
    }
}

impl From<RefPath<'_>> for Path {
    fn from(val: RefPath<'_>) -> Self {
        let mut dummy = Path::new(Key::String("".to_string()), None);
        let mut tail = &mut dummy;
        let mut current = Some(&val);
        while let Some(p) = current {
            tail.remainder = Some(Path::new(p.first.to_owned(), None).into());
            tail = tail.remainder.as_mut().unwrap();
            current = p.next();
        }
        *dummy.remainder.unwrap()
    }
}

impl Display for RefPath<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut paths = vec![&self.first];
        let mut remainder = &self.remainder;
        while let Some(p) = remainder {
            paths.push(&p.first);
            remainder = &p.remainder;
        }
        join(paths.iter(), ".", f)
    }
}

macro_rules! impl_path_eq {
    ($path1:ty, $path2:ty) => {
        impl PartialEq<$path1> for $path2 {
            fn eq(&self, other: &$path1) -> bool {
                let mut left = Some(self);
                let mut right = Some(other);
                while let (Some(l), Some(r)) = (left, right) {
                    if l.first != r.first {
                        return false;
                    }
                    left = l.next();
                    right = r.next();
                }
                left.is_none() && right.is_none()
            }
        }
    };
}

impl_path_eq!(crate::path::Path, RefPath<'_>);

impl_path_eq!(RefPath<'_>, crate::path::Path);