rd-ast 0.0.1

Canonical, producer-agnostic AST for R's Rd (documentation) format
Documentation
use super::*;

#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum LowerError {
    RootMustBeList {
        location: LowerLocation,
    },
    InvalidStringEncoding {
        location: LowerLocation,
        encoding: StringEncoding,
    },
    UnsupportedValue {
        location: LowerLocation,
    },
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LowerLocation {
    pub(super) path: RdPath,
    pub(super) tag: Option<String>,
    pub(super) attribute: Option<String>,
}

impl LowerLocation {
    pub fn path(&self) -> &RdPath {
        &self.path
    }
    pub fn tag(&self) -> Option<&str> {
        self.tag.as_deref()
    }
    pub fn attribute(&self) -> Option<&str> {
        self.attribute.as_deref()
    }
}

impl fmt::Display for LowerLocation {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.path.segments().is_empty() {
            f.write_str("document root")?;
        } else {
            self.path.fmt(f)?;
        }
        if self.tag.is_some() || self.attribute.is_some() {
            f.write_str(" (")?;
            if let Some(tag) = &self.tag {
                f.write_str(tag)?;
            }
            if let Some(attribute) = &self.attribute {
                if self.tag.is_some() {
                    f.write_str(", ")?;
                }
                write!(f, "@{attribute}")?;
            }
            f.write_str(")")?;
        }
        Ok(())
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum StringEncoding {
    Native,
    Utf8,
    Latin1,
    Bytes,
}

impl fmt::Display for StringEncoding {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            Self::Native => "Native",
            Self::Utf8 => "Utf8",
            Self::Latin1 => "Latin1",
            Self::Bytes => "Bytes",
        })
    }
}

impl LowerError {
    pub fn location(&self) -> &LowerLocation {
        match self {
            Self::RootMustBeList { location }
            | Self::InvalidStringEncoding { location, .. }
            | Self::UnsupportedValue { location } => location,
        }
    }
    pub fn encoding(&self) -> Option<StringEncoding> {
        match self {
            Self::InvalidStringEncoding { encoding, .. } => Some(*encoding),
            _ => None,
        }
    }
}

impl fmt::Display for LowerError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::RootMustBeList { location } => {
                write!(f, "root Rd object must be a list at {location}")
            }
            Self::InvalidStringEncoding { location, encoding } => {
                write!(f, "invalid {encoding} string encoding at {location}")
            }
            Self::UnsupportedValue { location } => {
                write!(f, "unsupported R value at {location}")
            }
        }
    }
}

impl std::error::Error for LowerError {}