1use std::path::PathBuf;
23
24pub fn innate_home() -> PathBuf {
26 dirs_next::home_dir()
27 .unwrap_or_else(|| PathBuf::from("."))
28 .join(".innate")
29}
30
31pub fn data_dir() -> PathBuf {
33 innate_home().join("data")
34}
35
36pub fn logs_dir() -> PathBuf {
38 innate_home().join("logs")
39}
40
41pub fn sessions_dir() -> PathBuf {
43 innate_home().join("sessions")
44}
45
46pub fn settings_path() -> PathBuf {
47 innate_home().join("settings.json")
48}
49
50pub fn default_db_path() -> PathBuf {
51 data_dir().join("personal.db")
52}
53
54pub fn daemon_pid_path() -> PathBuf {
55 data_dir().join("daemon.pid")
56}
57
58pub fn daemon_state_path() -> PathBuf {
59 data_dir().join("daemon_state.sqlite")
60}
61
62pub fn daemon_log_path() -> PathBuf {
63 logs_dir().join("daemon.log")
64}
65
66pub fn mcp_log_path() -> PathBuf {
67 logs_dir().join("mcp.log")
68}
69
70pub fn llm_trace_path() -> PathBuf {
73 logs_dir().join("llm_trace.log")
74}
75
76pub fn session_log_path() -> PathBuf {
77 sessions_dir().join("session.log")
78}
79
80pub fn backup_state_path() -> PathBuf {
82 data_dir().join("backup_state.json")
83}
84
85pub fn tmp_dir() -> PathBuf {
87 data_dir().join("tmp")
88}
89
90pub fn ensure_layout() {
94 ensure_layout_at(&innate_home());
95}
96
97pub fn ensure_layout_at(home: &std::path::Path) {
101 let data = home.join("data");
102 let logs = home.join("logs");
103 for dir in [&data, &logs, &home.join("sessions")] {
104 let _ = std::fs::create_dir_all(dir);
105 }
106
107 let moves: [(PathBuf, PathBuf); 8] = [
108 (home.join("personal.db"), data.join("personal.db")),
109 (home.join("personal.db-shm"), data.join("personal.db-shm")),
110 (home.join("personal.db-wal"), data.join("personal.db-wal")),
111 (home.join("daemon_state.sqlite"), data.join("daemon_state.sqlite")),
112 (home.join("daemon.pid"), data.join("daemon.pid")),
113 (home.join("backup_state.json"), data.join("backup_state.json")),
114 (home.join("daemon.log"), logs.join("daemon.log")),
115 (home.join("mcp.log"), logs.join("mcp.log")),
116 ];
117 for (legacy, target) in moves {
118 if legacy.exists() && !target.exists() {
119 let _ = std::fs::rename(&legacy, &target);
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn ensure_layout_migrates_legacy_flat_files() {
130 let tmp = tempfile::tempdir().unwrap();
131 let home = tmp.path();
132 for f in [
134 "personal.db",
135 "personal.db-wal",
136 "daemon_state.sqlite",
137 "daemon.pid",
138 "daemon.log",
139 "mcp.log",
140 ] {
141 std::fs::write(home.join(f), b"x").unwrap();
142 }
143
144 ensure_layout_at(home);
145
146 assert!(home.join("data").is_dir());
148 assert!(home.join("logs").is_dir());
149 assert!(home.join("sessions").is_dir());
150 assert!(home.join("data/personal.db").exists());
152 assert!(home.join("data/personal.db-wal").exists());
153 assert!(home.join("data/daemon_state.sqlite").exists());
154 assert!(home.join("data/daemon.pid").exists());
155 assert!(home.join("logs/daemon.log").exists());
156 assert!(home.join("logs/mcp.log").exists());
157 assert!(!home.join("personal.db").exists());
158 assert!(!home.join("mcp.log").exists());
159
160 ensure_layout_at(home);
162 assert!(home.join("data/personal.db").exists());
163 }
164}