Initial upload: DotMigrate dotfiles migration tool with CI/CD
Some checks failed
CI / test (push) Has been cancelled
CI / release (push) Has been cancelled

This commit is contained in:
2026-02-04 09:53:02 +00:00
parent 3108ed58ca
commit 1eba94b36a

181
src/config/mod.rs Normal file
View File

@@ -0,0 +1,181 @@
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use crate::cli::SyncBackend;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Dotfile {
pub path: PathBuf,
pub content_hash: Option<String>,
pub permissions: Option<u32>,
pub platform: Option<String>,
pub template: Option<bool>,
#[serde(default)]
pub excludes: Vec<String>,
}
impl Default for Dotfile {
fn default() -> Self {
Self {
path: PathBuf::new(),
content_hash: None,
permissions: None,
platform: None,
template: Some(false),
excludes: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncConfig {
pub backend: Option<SyncBackend>,
pub remote: Option<String>,
pub branch: String,
pub path: PathBuf,
#[serde(default)]
pub excludes: Vec<String>,
}
impl Default for SyncConfig {
fn default() -> Self {
Self {
backend: Some(SyncBackend::Git),
remote: None,
branch: "main".to_string(),
path: PathBuf::from("dotfiles"),
excludes: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MergeConfig {
pub strategy: Option<String>,
pub keep_backup: bool,
pub auto_resolve: bool,
}
impl Default for MergeConfig {
fn default() -> Self {
Self {
strategy: Some("ask".to_string()),
keep_backup: true,
auto_resolve: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectConfig {
pub patterns: Vec<String>,
pub exclude_patterns: Vec<String>,
pub include_hidden: bool,
pub scan_depth: usize,
}
impl Default for DetectConfig {
fn default() -> Self {
Self {
patterns: vec![
".*".to_string(),
".config/*".to_string(),
".local/*".to_string(),
],
exclude_patterns: vec![
".git".to_string(),
".gitignore".to_string(),
"node_modules".to_string(),
".cache".to_string(),
],
include_hidden: true,
scan_depth: 5,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupConfig {
pub directory: PathBuf,
pub max_backups: usize,
pub timestamp_format: String,
}
impl Default for BackupConfig {
fn default() -> Self {
Self {
directory: dirs::home_dir()
.map(|p| p.join(".dotmigrate/backups"))
.unwrap_or_else(|| PathBuf::from(".dotmigrate/backups")),
max_backups: 10,
timestamp_format: "%Y-%m-%d_%H-%M-%S".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub version: String,
pub sync: SyncConfig,
pub merge: MergeConfig,
pub detect: DetectConfig,
pub backup: BackupConfig,
#[serde(default)]
pub dotfiles: Vec<Dotfile>,
}
impl Default for Config {
fn default() -> Self {
Self {
version: "1.0".to_string(),
sync: SyncConfig::default(),
merge: MergeConfig::default(),
detect: DetectConfig::default(),
backup: BackupConfig::default(),
dotfiles: Vec::new(),
}
}
}
impl Config {
pub fn default_for_backend(backend: SyncBackend) -> Self {
let mut config = Self::default();
config.sync.backend = Some(backend);
config
}
pub fn default_path() -> Result<PathBuf, std::io::Error> {
dirs::home_dir()
.map(|p| p.join(".dotmigrate/config.yml"))
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Home directory not found"))
}
pub fn load(path: &Path) -> Result<Self, anyhow::Error> {
let content = std::fs::read_to_string(path)?;
let config: Config = serde_yaml::from_str(&content)?;
Ok(config)
}
pub fn save(&self, path: &Path) -> Result<(), anyhow::Error> {
let content = serde_yaml::to_string(self)?;
std::fs::write(path, content)?;
Ok(())
}
pub fn find_config() -> Option<PathBuf> {
let current_dir = std::env::current_dir().ok()?;
let home_dir = dirs::home_dir()?;
for dir in [current_dir, home_dir] {
let config_path = dir.join(".dotmigrate/config.yml");
if config_path.exists() {
return Some(config_path);
}
let alt_path = dir.join("dotmigrate.yml");
if alt_path.exists() {
return Some(alt_path);
}
}
None
}
}