plugx-input 0.3.1

simple and flexible data-structure with diff, merge, and validation.
Documentation
use serde::Serialize;
use std::{
    fmt::{Display, Formatter, Result},
    ops::Index,
};

#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct InputPosition {
    inner: Vec<InputPositionType>,
}

impl InputPosition {
    pub fn new() -> Self {
        Self { inner: Vec::new() }
    }

    pub fn new_with_index(&self, index: usize) -> Self {
        let mut new = self.clone();
        new.add_index(index);
        new
    }

    pub fn new_with_key(&self, key: &str) -> Self {
        let mut new = self.clone();
        new.add_key(key);
        new
    }

    pub fn add_index(&mut self, index: usize) {
        self.inner.push(InputPositionType::Index(index))
    }

    pub fn add_key(&mut self, key: &str) {
        self.inner.push(InputPositionType::Key(key.to_string()))
    }

    pub fn add<T: Into<InputPositionType>>(&mut self, key_or_index: T) {
        self.inner.push(key_or_index.into())
    }

    pub fn is_empty(&self) -> bool {
        self.inner.is_empty()
    }
}

pub fn new() -> InputPosition {
    InputPosition::new()
}

impl Default for InputPosition {
    fn default() -> Self {
        Self::new()
    }
}

impl Display for InputPosition {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        if self.inner.len() == 1 {
            f.write_str(self.inner[0].to_string().as_str())
        } else {
            self.inner
                .iter()
                .try_for_each(|position_type| f.write_str(format!("[{position_type}]").as_str()))
        }
    }
}

impl Index<usize> for InputPosition {
    type Output = InputPositionType;

    fn index(&self, index: usize) -> &Self::Output {
        self.inner.index(index)
    }
}

#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum InputPositionType {
    Key(String),
    Index(usize),
}

impl InputPositionType {
    pub fn new<T: Into<Self>>(value: T) -> Self {
        value.into()
    }
}

impl From<usize> for InputPositionType {
    fn from(index: usize) -> Self {
        Self::Index(index)
    }
}

impl From<&str> for InputPositionType {
    fn from(key: &str) -> Self {
        Self::Key(key.to_string())
    }
}

impl From<String> for InputPositionType {
    fn from(key: String) -> Self {
        Self::Key(key)
    }
}

impl Display for InputPositionType {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        f.write_str(
            match self {
                Self::Key(key) => key.to_string(),
                Self::Index(index) => format!("{index}"),
            }
            .as_str(),
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn done() {
        let mut pos = new();
        pos.add_index(0);
        assert_eq!(pos[0], InputPositionType::new(0));
        assert_eq!(format!("{pos}"), "0".to_string());
        pos.add_key("foo");
        assert_eq!(pos[0], InputPositionType::new(0));
        assert_eq!(pos[1], InputPositionType::new("foo"));
        assert_eq!(format!("{pos}"), "[0][foo]".to_string());
    }
}