1use anyhow::{Context, Result};
6use serde::{Deserialize, Serialize};
7use std::path::PathBuf;
8
9#[derive(Debug, Serialize, Deserialize, Clone)]
10pub struct ValetConfig {
11 pub work_tree: String,
13 pub remote: String,
15 pub bare_path: String,
17 pub tracked: Vec<String>,
19 #[serde(default = "default_branch")]
21 pub branch: String,
22}
23
24fn default_branch() -> String {
25 "main".to_string()
26}
27
28pub 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
36pub 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
43pub fn config_path_for(project_id: &str) -> Result<PathBuf> {
45 Ok(valets_dir()?.join(project_id).join("config.toml"))
46}
47
48pub 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
58pub 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
67pub 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
76mod 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}