gitbox 2.1.3

Git toolbox to simplify adoption of conventional commits and semantic version, among other things.
Documentation
use std::{
    fs::{create_dir_all, read_to_string},
    io::Write,
    path::Path,
};

use crate::{
    application::{
        manager::gitextra_egress_manager::GitExtraEgressManager,
        manager::gitinfo_ingress_manager::GitInfoIngressManager,
    },
    usecase::type_aliases::AnyError,
};
use crate::{
    domain::constant::DEFAULT_COMMIT_TYPES,
    infrastructure::helper::{
        gitextra_egress_helper::GitExtraEgressHelper,
        gitextra_ingress_helper::GitExtraIngressHelper,
    },
};

const EXTRA_DIR_PATH: &str = "extra";
const TYPES_FILE_PATH: &str = "types.txt";
const SCOPES_FILE_PATH: &str = "scopes.txt";

pub struct GitExtraManagerImpl<'a> {
    gitinfo_manager: &'a dyn GitInfoIngressManager,
}

impl<'b: 'a, 'a> GitExtraManagerImpl<'a> {
    pub fn new(gitinfo_manager: &'b dyn GitInfoIngressManager) -> Self {
        GitExtraManagerImpl { gitinfo_manager }
    }

    fn write_file(&self, path: &Path, content: String) -> Result<(), AnyError> {
        if let Some(parent) = path.parent() {
            create_dir_all(parent)?;
        }
        let mut file = std::fs::File::options()
            .write(true)
            .create(true)
            .truncate(true)
            .open(path)?;
        writeln!(&mut file, "{}", content.trim())?;
        Ok(())
    }

    fn append_to_file(&self, path: &Path, content: &str) -> Result<(), AnyError> {
        let mut f = std::fs::File::options().append(true).open(path).unwrap();
        write!(f, "\n{}", content)?;
        Ok(())
    }
}

impl GitExtraEgressManager for GitExtraManagerImpl<'_> {
    fn update_types(&self, types: Box<dyn Iterator<Item = String>>) -> Result<(), AnyError> {
        let content = types.fold(String::new(), |acc, e| acc + "\n" + &e);
        let path = Path::new(&self.gitinfo_manager.git_dir()?)
            .join(EXTRA_DIR_PATH)
            .join(TYPES_FILE_PATH);
        self.write_file(&path, content)
    }

    fn update_scopes(&self, scopes: Box<dyn Iterator<Item = String>>) -> Result<(), AnyError> {
        let content = scopes.fold(String::new(), |acc, e| acc + "\n" + &e);
        let path = Path::new(&self.gitinfo_manager.git_dir()?)
            .join(EXTRA_DIR_PATH)
            .join(SCOPES_FILE_PATH);
        self.write_file(&path, content)
    }
}

impl GitExtraIngressHelper for GitExtraManagerImpl<'_> {
    fn get_types(&self) -> Result<Vec<String>, AnyError> {
        let path = Path::new(&self.gitinfo_manager.git_dir()?)
            .join(EXTRA_DIR_PATH)
            .join(TYPES_FILE_PATH);
        match path.try_exists() {
            Ok(true) => Ok(DEFAULT_COMMIT_TYPES
                .iter()
                .map(|it| it.to_owned())
                .chain(
                    read_to_string(path)?
                        .split('\n')
                        .filter(|it| !it.is_empty() && !DEFAULT_COMMIT_TYPES.contains(it)),
                )
                .map(|it| it.to_string())
                .collect()),
            Ok(false) => {
                self.update_types(Box::new(
                    DEFAULT_COMMIT_TYPES
                        .map(|it| it.to_string())
                        .into_iter(),
                ))?;
                Ok(DEFAULT_COMMIT_TYPES.map(|it| it.to_string()).to_vec())
            }
            Err(e) => Err(e.into()),
        }
    }

    fn get_scopes(&self) -> Result<Vec<String>, AnyError> {
        let path = Path::new(&self.gitinfo_manager.git_dir()?)
            .join(EXTRA_DIR_PATH)
            .join(SCOPES_FILE_PATH);
        match path.try_exists() {
            Ok(true) => Ok(read_to_string(path)?
                .split('\n')
                .filter(|it| !it.is_empty())
                .map(|it| it.to_string())
                .collect()),
            Ok(false) => {
                self.update_scopes(Box::new(Vec::new().into_iter()))?;
                Ok(Vec::new())
            }
            Err(e) => Err(e.into()),
        }
    }
}

impl GitExtraEgressHelper for GitExtraManagerImpl<'_> {
    fn append_type(&self, new_type: &str) -> Result<(), AnyError> {
        let path = Path::new(&self.gitinfo_manager.git_dir()?)
            .join(EXTRA_DIR_PATH)
            .join(TYPES_FILE_PATH);
        self.append_to_file(&path, new_type)
    }

    fn append_scope(&self, new_scope: &str) -> Result<(), AnyError> {
        let path = Path::new(&self.gitinfo_manager.git_dir()?)
            .join(EXTRA_DIR_PATH)
            .join(SCOPES_FILE_PATH);
        self.append_to_file(&path, new_scope)
    }
}