ix-core 0.1.0

Ixchel core library: registries, validation, sync orchestration, and context building
Documentation
use std::str::FromStr;

use thiserror::Error;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EntityKind {
    Decision,
    Issue,
    Idea,
    Report,
    Source,
    Citation,
    Agent,
    Session,
}

impl EntityKind {
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Decision => "decision",
            Self::Issue => "issue",
            Self::Idea => "idea",
            Self::Report => "report",
            Self::Source => "source",
            Self::Citation => "citation",
            Self::Agent => "agent",
            Self::Session => "session",
        }
    }

    #[must_use]
    pub const fn directory_name(self) -> &'static str {
        match self {
            Self::Decision => "decisions",
            Self::Issue => "issues",
            Self::Idea => "ideas",
            Self::Report => "reports",
            Self::Source => "sources",
            Self::Citation => "citations",
            Self::Agent => "agents",
            Self::Session => "sessions",
        }
    }

    #[must_use]
    pub const fn id_prefix(self) -> &'static str {
        match self {
            Self::Decision => "dec",
            Self::Issue => "iss",
            Self::Idea => "idea",
            Self::Report => "rpt",
            Self::Source => "src",
            Self::Citation => "cite",
            Self::Agent => "agt",
            Self::Session => "ses",
        }
    }
}

#[derive(Debug, Error)]
pub enum ParseEntityKindError {
    #[error("Unknown entity kind: {0}")]
    UnknownKind(String),
}

impl FromStr for EntityKind {
    type Err = ParseEntityKindError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let normalized = s.trim().to_ascii_lowercase();
        match normalized.as_str() {
            "decision" | "decisions" => Ok(Self::Decision),
            "issue" | "issues" => Ok(Self::Issue),
            "idea" | "ideas" => Ok(Self::Idea),
            "report" | "reports" => Ok(Self::Report),
            "source" | "sources" => Ok(Self::Source),
            "citation" | "citations" => Ok(Self::Citation),
            "agent" | "agents" => Ok(Self::Agent),
            "session" | "sessions" => Ok(Self::Session),
            _ => Err(ParseEntityKindError::UnknownKind(s.to_string())),
        }
    }
}

#[must_use]
pub fn kind_from_id(id: &str) -> Option<EntityKind> {
    let (prefix, _) = id.split_once('-')?;
    match prefix {
        "dec" => Some(EntityKind::Decision),
        "iss" | "bd" => Some(EntityKind::Issue),
        "idea" => Some(EntityKind::Idea),
        "rpt" => Some(EntityKind::Report),
        "src" => Some(EntityKind::Source),
        "cite" => Some(EntityKind::Citation),
        "agt" => Some(EntityKind::Agent),
        "ses" => Some(EntityKind::Session),
        _ => None,
    }
}

#[must_use]
pub fn looks_like_entity_id(id: &str) -> bool {
    ix_id::parse_id(id).is_ok()
}