ix-core 0.1.0

Ixchel core library: registries, validation, sync orchestration, and context building
Documentation
use std::path::{Path, PathBuf};

use anyhow::{Context, Result};

use crate::entity::{EntityKind, kind_from_id};

pub const IXCHEL_DIR_NAME: &str = ".ixchel";

#[derive(Debug, Clone)]
pub struct IxchelPaths {
    repo_root: PathBuf,
}

impl IxchelPaths {
    #[must_use]
    pub const fn new(repo_root: PathBuf) -> Self {
        Self { repo_root }
    }

    #[must_use]
    pub fn repo_root(&self) -> &Path {
        &self.repo_root
    }

    #[must_use]
    pub fn ixchel_dir(&self) -> PathBuf {
        self.repo_root.join(IXCHEL_DIR_NAME)
    }

    #[must_use]
    pub fn config_path(&self) -> PathBuf {
        self.ixchel_dir().join("config.toml")
    }

    #[must_use]
    pub fn data_dir(&self) -> PathBuf {
        self.ixchel_dir().join("data")
    }

    #[must_use]
    pub fn kind_dir(&self, kind: EntityKind) -> PathBuf {
        self.ixchel_dir().join(kind.directory_name())
    }

    #[must_use]
    pub fn entity_path(&self, id: &str) -> Option<PathBuf> {
        let kind = kind_from_id(id)?;
        Some(self.kind_dir(kind).join(format!("{id}.md")))
    }

    pub fn ensure_layout(&self) -> Result<()> {
        std::fs::create_dir_all(self.data_dir())
            .with_context(|| format!("Failed to create {}", self.data_dir().display()))?;

        for kind in [
            EntityKind::Decision,
            EntityKind::Issue,
            EntityKind::Idea,
            EntityKind::Report,
            EntityKind::Source,
            EntityKind::Citation,
            EntityKind::Agent,
            EntityKind::Session,
        ] {
            let dir = self.kind_dir(kind);
            std::fs::create_dir_all(&dir)
                .with_context(|| format!("Failed to create {}", dir.display()))?;
        }

        Ok(())
    }
}

#[must_use]
pub fn find_git_root(start: &Path) -> Option<PathBuf> {
    let mut current = Some(start);
    while let Some(dir) = current {
        if dir.join(".git").exists() {
            return Some(dir.to_path_buf());
        }
        current = dir.parent();
    }
    None
}