mntn 3.2.0

A Rust-based command-line tool for dotfiles management with profiles.
Documentation
pub mod config;
pub mod encrypted;
pub mod package;

use crate::errors::Result;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, collections::HashMap, path::PathBuf};

pub(crate) trait RegistryEntryLike {
    fn is_enabled(&self) -> bool;
}

#[macro_export]
macro_rules! impl_registry_entry_like {
    ($t:ty) => {
        impl RegistryEntryLike for $t {
            fn is_enabled(&self) -> bool {
                self.enabled
            }
        }
    };
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct Registry<T> {
    pub version: String,
    pub entries: HashMap<String, T>,
}

impl<T> Registry<T>
where
    T: RegistryEntryLike + Clone + Serialize + for<'a> Deserialize<'a>,
{
    pub(crate) fn load_or_create(path: &PathBuf) -> Result<Self>
    where
        Self: Default,
    {
        if path.exists() {
            let content = std::fs::read_to_string(path)?;
            let registry: Registry<T> = serde_json::from_str(&content)?;
            Ok(registry)
        } else {
            let registry = Self::default();
            registry.save(path)?;
            Ok(registry)
        }
    }

    pub(crate) fn save(&self, path: &PathBuf) -> Result<()> {
        if let Some(parent) = path.parent() {
            std::fs::create_dir_all(parent)?;
        }

        let sorted_entries: BTreeMap<&String, &T> = self.entries.iter().collect();
        let sorted_registry = serde_json::json!({
            "version": self.version,
            "entries": sorted_entries
        });

        let content = serde_json::to_string_pretty(&sorted_registry)?;
        std::fs::write(path, content)?;
        Ok(())
    }

    pub(crate) fn get_enabled_entries(&self) -> impl Iterator<Item = (&String, &T)> {
        self.entries.iter().filter(|(_, e)| e.is_enabled())
    }
}