whiteout/config/
project.rs1use anyhow::{Context, Result};
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use super::ConfigData;
6
7#[derive(Debug, Clone)]
8pub struct Config {
9 pub data: ConfigData,
10 pub path: PathBuf,
11}
12
13impl Config {
14 pub fn load_or_default(project_root: impl AsRef<Path>) -> Result<Self> {
15 let config_path = project_root.as_ref().join(".whiteout").join("config.toml");
16
17 let data = if config_path.exists() {
18 let content = fs::read_to_string(&config_path)
19 .context("Failed to read config file")?;
20 toml::from_str(&content).context("Failed to parse config file")?
21 } else {
22 ConfigData::default()
23 };
24
25 Ok(Self {
26 data,
27 path: config_path,
28 })
29 }
30
31 pub fn init(project_root: impl AsRef<Path>) -> Result<()> {
32 let whiteout_dir = project_root.as_ref().join(".whiteout");
33 fs::create_dir_all(&whiteout_dir).context("Failed to create .whiteout directory")?;
34
35 let config_path = whiteout_dir.join("config.toml");
36 if !config_path.exists() {
37 let initial_config = ConfigData::default();
38 let content = toml::to_string_pretty(&initial_config)
39 .context("Failed to serialize initial config")?;
40 fs::write(&config_path, content).context("Failed to write initial config")?;
41 }
42
43 Self::setup_git_config(project_root.as_ref())?;
44
45 Ok(())
46 }
47
48 pub fn save(&self) -> Result<()> {
49 let content = toml::to_string_pretty(&self.data)
50 .context("Failed to serialize config")?;
51
52 fs::create_dir_all(self.path.parent().unwrap())
53 .context("Failed to create config directory")?;
54
55 fs::write(&self.path, content)
56 .context("Failed to write config file")?;
57
58 Ok(())
59 }
60
61 pub fn set(&mut self, key: &str, value: &str) -> Result<()> {
62 match key {
63 "encryption.enabled" => {
64 self.data.encryption.enabled = value.parse()
65 .context("Invalid boolean value")?;
66 }
67 "git.auto_sync" => {
68 self.data.git.auto_sync = value.parse()
69 .context("Invalid boolean value")?;
70 }
71 "git.pre_commit_check" => {
72 self.data.git.pre_commit_check = value.parse()
73 .context("Invalid boolean value")?;
74 }
75 _ => anyhow::bail!("Unknown config key: {}", key),
76 }
77
78 self.save()?;
79 Ok(())
80 }
81
82 pub fn get(&self, key: &str) -> Result<String> {
83 let value = match key {
84 "encryption.enabled" => self.data.encryption.enabled.to_string(),
85 "git.auto_sync" => self.data.git.auto_sync.to_string(),
86 "git.pre_commit_check" => self.data.git.pre_commit_check.to_string(),
87 _ => anyhow::bail!("Unknown config key: {}", key),
88 };
89
90 Ok(value)
91 }
92
93 fn setup_git_config(project_root: &Path) -> Result<()> {
94 let gitattributes_path = project_root.join(".gitattributes");
95 let mut content = if gitattributes_path.exists() {
96 fs::read_to_string(&gitattributes_path)?
97 } else {
98 String::new()
99 };
100
101 if !content.contains("filter=whiteout") {
102 if !content.is_empty() && !content.ends_with('\n') {
103 content.push('\n');
104 }
105 content.push_str("* filter=whiteout\n");
106 fs::write(&gitattributes_path, content)?;
107 }
108
109 Ok(())
110 }
111}
112
113impl Default for Config {
114 fn default() -> Self {
115 Self {
116 data: ConfigData::default(),
117 path: PathBuf::from(".whiteout/config.toml"),
118 }
119 }
120}