limit_cli/
project_settings.rs1use crate::error::CliError;
6use rusqlite::{params, Connection};
7use std::collections::hash_map::DefaultHasher;
8use std::hash::{Hash, Hasher};
9use std::path::PathBuf;
10
11pub struct ProjectSettings {
12 db_path: PathBuf,
13}
14
15impl ProjectSettings {
16 pub fn new() -> Result<Self, CliError> {
17 let home_dir = dirs::home_dir()
18 .ok_or_else(|| CliError::ConfigError("Failed to get home directory".to_string()))?;
19 let limit_dir = home_dir.join(".limit");
20 std::fs::create_dir_all(&limit_dir).map_err(|e| {
21 CliError::ConfigError(format!("Failed to create .limit directory: {}", e))
22 })?;
23
24 let db_path = limit_dir.join("tracking.db");
25 let settings = Self { db_path };
26 settings.init_db()?;
27 Ok(settings)
28 }
29
30 fn init_db(&self) -> Result<(), CliError> {
31 let conn = Connection::open(&self.db_path)
32 .map_err(|e| CliError::ConfigError(format!("Failed to open database: {}", e)))?;
33
34 conn.execute(
35 "CREATE TABLE IF NOT EXISTS project_settings (
36 project_hash TEXT PRIMARY KEY,
37 warm_enabled INTEGER NOT NULL DEFAULT 0,
38 warmed_at TEXT
39 )",
40 [],
41 )
42 .map_err(|e| CliError::ConfigError(format!("Failed to create table: {}", e)))?;
43
44 Ok(())
45 }
46
47 pub fn hash_project(project_path: &std::path::Path) -> String {
49 let canonical = project_path
50 .canonicalize()
51 .unwrap_or_else(|_| project_path.to_path_buf());
52
53 let mut hasher = DefaultHasher::new();
54 canonical.to_string_lossy().hash(&mut hasher);
55 format!("{:x}", hasher.finish())
56 }
57
58 pub fn is_warm_enabled(&self, project_path: &std::path::Path) -> bool {
60 let hash = Self::hash_project(project_path);
61 let conn = match Connection::open(&self.db_path) {
62 Ok(c) => c,
63 Err(_) => return false,
64 };
65
66 let enabled: i64 = conn
67 .query_row(
68 "SELECT warm_enabled FROM project_settings WHERE project_hash = ?",
69 params![&hash],
70 |row| row.get(0),
71 )
72 .unwrap_or(0);
73
74 enabled != 0
75 }
76
77 pub fn set_warm_enabled(&self, project_path: &std::path::Path) -> Result<(), CliError> {
79 let hash = Self::hash_project(project_path);
80 let conn = Connection::open(&self.db_path)
81 .map_err(|e| CliError::ConfigError(format!("Failed to open database: {}", e)))?;
82
83 let now = chrono::Utc::now().to_rfc3339();
84
85 conn.execute(
86 "INSERT OR REPLACE INTO project_settings (project_hash, warm_enabled, warmed_at) VALUES (?1, 1, ?2)",
87 params![&hash, &now],
88 )
89 .map_err(|e| CliError::ConfigError(format!("Failed to update settings: {}", e)))?;
90
91 Ok(())
92 }
93}
94
95impl Default for ProjectSettings {
96 fn default() -> Self {
97 Self::new().expect("Failed to create ProjectSettings")
98 }
99}