pipa-lang 1.0.0-alpha.1

A tiny template language
Documentation
// SPDX-FileCopyrightText: Copyright 2026 olav@occy.org
// SPDX-License-Identifier: MPL-2.0

use crate::value::keys::Keys;
use bstr::ByteSlice;

#[derive(Clone, PartialEq)]
pub(crate) struct Label(Vec<u8>);

impl core::fmt::Debug for Label {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(&self.to_string())
    }
}

impl core::fmt::Display for Label {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(&String::from_utf8_lossy(&self.0))
    }
}

#[cfg(test)]
impl<T: Into<Vec<u8>>> From<T> for Label {
    fn from(value: T) -> Self {
        Self(value.into())
    }
}

impl Label {
    pub(crate) fn new<T: Into<Vec<u8>>>(value: T) -> Option<Self> {
        Some(Self(value.into()))
            .filter(|l| !l.0.contains_str("::"))
            .filter(|l| !l.as_name().is_empty())
    }

    pub(crate) fn as_name(&self) -> &[u8] {
        match self.0.find_byte(b':') {
            None => self.as_bytes(),
            Some(_) if self.0.len() == 1 => &[],
            Some(i) if i == self.0.len() - 1 => &self.0[..i],

            Some(i) => match self.0[(i + 1)..].rfind_byte(b':') {
                None => &self.0[(i + 1)..],
                Some(j) => &self.0[(i + 1)..(i + 1 + j)],
            },
        }
    }

    pub(crate) fn as_keys(&self) -> Keys {
        Keys::parse(self.as_name())
    }

    pub(crate) fn as_prefix(&self) -> Option<&[u8]> {
        self.0.find_byte(b':').and_then(|i| self.0.get(..i))
    }

    pub(crate) fn as_suffix(&self) -> Option<&[u8]> {
        self.0.rfind_byte(b':').and_then(|i| self.0.get((i + 1)..))
    }

    pub(crate) fn as_bytes(&self) -> &[u8] {
        &self.0
    }
}

#[cfg(test)]
mod test {
    use crate::lang::label::Label;
    use crate::value::keys::Keys;

    #[test]
    fn test() {
        assert_eq!(Label::new(""), None);
        assert_eq!(Label::new("::"), None);
        assert_eq!(Label::new("a").unwrap().as_name(), b"a");
        assert_eq!(Label::new("a:").unwrap().as_name(), b"a");
        assert_eq!(Label::new("a:").unwrap().as_prefix().unwrap(), b"a");
        assert_eq!(Label::new(":a").unwrap().as_name(), b"a");
        assert_eq!(Label::new("fn:a").unwrap().as_name(), b"a");
        assert_eq!(Label::new("abc:").unwrap().as_name(), b"abc");
        assert_eq!(Label::new("a:foo").unwrap().as_name(), b"foo");
        assert_eq!(Label::new("a:b:c").unwrap().as_prefix().unwrap(), b"a");
        assert_eq!(Label::new("a:b:c").unwrap().as_name(), b"b");
        assert_eq!(Label::new("a:b:c").unwrap().as_suffix().unwrap(), b"c");
        assert_eq!(Label::new("a").unwrap().as_keys(), Keys::from("a"));
        assert_eq!(Label::new("a:b").unwrap().as_keys(), Keys::from("b"));
        assert_eq!(Label::new("a:b:c").unwrap().as_keys(), Keys::from("b"));
        assert_eq!(Label::new(":c").unwrap().as_keys(), Keys::from("c"));
    }
}