Skip to main content

git_valet/
config.rs

1//! Configuration management for git-valet.
2//!
3//! Stores per-project configuration in `~/.git-valets/<project-id>/config.toml`.
4
5use anyhow::{Context, Result};
6use serde::{Deserialize, Serialize};
7use std::path::PathBuf;
8
9#[derive(Debug, Serialize, Deserialize, Clone)]
10pub struct ValetConfig {
11    /// Absolute path of the main repo (work-tree)
12    pub work_tree: String,
13    /// Remote of the valet repo
14    pub remote: String,
15    /// Absolute path of the valet bare repo
16    pub bare_path: String,
17    /// Tracked files/directories
18    pub tracked: Vec<String>,
19    /// Valet repo branch (default: "main")
20    #[serde(default = "default_branch")]
21    pub branch: String,
22}
23
24fn default_branch() -> String {
25    "main".to_string()
26}
27
28/// Returns the ~/.git-valets/ directory
29pub fn valets_dir() -> Result<PathBuf> {
30    let home = dirs::home_dir().context("Could not find home directory")?;
31    let dir = home.join(".git-valets");
32    std::fs::create_dir_all(&dir)?;
33    Ok(dir)
34}
35
36/// Generates a unique ID based on the main remote URL (BLAKE3, 16 hex chars)
37pub fn project_id(origin_url: &str) -> String {
38    let hash = blake3::hash(origin_url.as_bytes());
39    let bytes = hash.as_bytes();
40    hex::encode(&bytes[..8])
41}
42
43/// Returns the config file path for the current project
44pub fn config_path_for(project_id: &str) -> Result<PathBuf> {
45    Ok(valets_dir()?.join(project_id).join("config.toml"))
46}
47
48/// Loads the valet config for the current repo
49pub fn load(main_remote: &str) -> Result<ValetConfig> {
50    let id = project_id(main_remote);
51    let path = config_path_for(&id)?;
52    let content = std::fs::read_to_string(&path)
53        .context("Valet repo not initialized. Run: git valet init <remote> <files>")?;
54    let config: ValetConfig = toml::from_str(&content).context("Valet config is corrupted")?;
55    Ok(config)
56}
57
58/// Saves the valet config
59pub fn save(config: &ValetConfig, project_id: &str) -> Result<()> {
60    let path = config_path_for(project_id)?;
61    std::fs::create_dir_all(path.parent().context("Invalid config path")?)?;
62    let content = toml::to_string_pretty(config)?;
63    std::fs::write(&path, content)?;
64    Ok(())
65}
66
67/// Removes the valet config
68pub fn remove(project_id: &str) -> Result<()> {
69    let dir = valets_dir()?.join(project_id);
70    if dir.exists() {
71        std::fs::remove_dir_all(&dir)?;
72    }
73    Ok(())
74}
75
76/// Hex-encodes a byte slice (lightweight replacement for the `hex` crate)
77mod hex {
78    use std::fmt::Write;
79
80    pub fn encode(bytes: &[u8]) -> String {
81        bytes.iter().fold(String::with_capacity(bytes.len() * 2), |mut s, b| {
82            let _ = write!(s, "{b:02x}");
83            s
84        })
85    }
86}