skyspell_core 4.0.0

skyspell core library
Documentation
use std::borrow::Cow;
use std::fmt::Display;
use std::path::{Path, PathBuf};

use anyhow::{anyhow, Context, Result};
use ignore::{Walk, WalkBuilder};
use serde::{Deserialize, Serialize};

use crate::{global_path, IgnoreStore, SkipFile};

pub const SKYSPELL_LOCAL_IGNORE: &str = "skyspell-ignore.toml";

#[derive(Debug, Clone)]
pub struct Project {
    path: ProjectPath,
    skip_file: SkipFile,
}

impl Project {
    pub fn new(path: &Path) -> Result<Self> {
        let skip_file = SkipFile::new(path)?;
        let path = ProjectPath::new(path)?;
        Ok(Self { path, skip_file })
    }

    pub fn path(&self) -> &ProjectPath {
        &self.path
    }

    pub fn as_str(&self) -> Cow<str> {
        self.path.as_str()
    }

    pub fn as_relative_path(&self, path: &str) -> Result<RelativePath> {
        RelativePath::new(self.path(), Path::new(path))
    }

    pub fn get_relative_path(&self, path: &Path) -> Result<RelativePath> {
        RelativePath::new(self.path(), path)
    }

    pub fn ignore_path(&self) -> PathBuf {
        let path = self.path().as_ref();
        path.join(SKYSPELL_LOCAL_IGNORE)
    }

    pub fn ignore_store(&self) -> Result<IgnoreStore> {
        let local_path = self.path.0.join(SKYSPELL_LOCAL_IGNORE);
        let global_path = global_path()?;

        IgnoreStore::load(global_path, local_path)
    }

    pub fn skip_file(&self) -> &SkipFile {
        &self.skip_file
    }

    pub fn walk(&self) -> Result<Walk> {
        Ok(WalkBuilder::new(self.path().as_ref()).build())
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProjectPath(PathBuf);

impl ProjectPath {
    pub fn new(project_path: &Path) -> Result<Self> {
        let path = std::fs::canonicalize(project_path).with_context(|| {
            anyhow!(
                "Could not canonicalize project path: {}",
                project_path.display()
            )
        })?;
        Ok(ProjectPath(path))
    }

    pub fn as_str(&self) -> Cow<str> {
        self.0.to_string_lossy()
    }
}

impl AsRef<Path> for ProjectPath {
    fn as_ref(&self) -> &Path {
        &self.0
    }
}

impl Display for ProjectPath {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0.display())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RelativePath(PathBuf);

impl RelativePath {
    pub fn new(project_path: &ProjectPath, source_path: &Path) -> Result<Self> {
        let source_path = std::fs::canonicalize(source_path).with_context(|| {
            anyhow!(
                "Could not canonicalize relative path: {}",
                source_path.display()
            )
        })?;
        let path = pathdiff::diff_paths(&source_path, project_path.as_ref()).ok_or_else(|| {
            anyhow!(
                "Could not diff paths '{}' and '{}'",
                source_path.display(),
                project_path,
            )
        })?;
        Ok(Self(path))
    }

    /// Returns a relative path without checking that
    ///  - it's relative to an existing project
    ///  - it exists
    pub fn from_path_unchecked(path: PathBuf) -> Self {
        Self(path)
    }

    pub fn as_str(&self) -> Cow<str> {
        self.0.to_string_lossy()
    }

    pub fn file_name(&self) -> Option<Cow<str>> {
        self.0.file_name().map(|x| x.to_string_lossy())
    }

    pub fn extension(&self) -> Option<Cow<str>> {
        self.0.extension().map(|x| x.to_string_lossy())
    }
}

impl AsRef<Path> for RelativePath {
    fn as_ref(&self) -> &Path {
        &self.0
    }
}

impl Display for RelativePath {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0.display())
    }
}