1use std::path::{Path, PathBuf};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Paths {
9 state_dir: PathBuf,
10}
11
12impl Paths {
13 pub fn new(state_dir: impl Into<PathBuf>) -> Self {
14 Self {
15 state_dir: state_dir.into(),
16 }
17 }
18
19 pub fn from_env() -> Self {
21 let base = std::env::var_os("XDG_STATE_HOME")
22 .map(PathBuf::from)
23 .filter(|p| p.is_absolute())
24 .unwrap_or_else(|| {
25 std::env::var_os("HOME")
26 .map(PathBuf::from)
27 .unwrap_or_else(|| PathBuf::from("."))
28 .join(".local/state")
29 });
30 Self::new(base.join("stackless"))
31 }
32
33 pub fn state_dir(&self) -> &Path {
34 &self.state_dir
35 }
36
37 pub fn db_path(&self) -> PathBuf {
38 self.state_dir.join("state.db")
39 }
40
41 pub fn socket_path(&self) -> PathBuf {
42 self.state_dir.join("daemon.sock")
43 }
44
45 pub fn daemon_log(&self) -> PathBuf {
46 self.state_dir.join("daemon.log")
47 }
48
49 pub fn spawn_lock(&self) -> PathBuf {
50 self.state_dir.join("daemon.spawn.lock")
51 }
52
53 pub fn logs_dir(&self, instance: &str) -> PathBuf {
54 self.state_dir.join("logs").join(instance)
55 }
56
57 pub fn persistence_marker(&self) -> PathBuf {
59 self.state_dir.join("daemon.persistence")
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn layout_under_state_dir() {
69 let paths = Paths::new("/tmp/stackless-test");
70 assert_eq!(paths.state_dir(), Path::new("/tmp/stackless-test"));
71 assert_eq!(
72 paths.db_path(),
73 PathBuf::from("/tmp/stackless-test/state.db")
74 );
75 assert_eq!(
76 paths.socket_path(),
77 PathBuf::from("/tmp/stackless-test/daemon.sock")
78 );
79 assert_eq!(
80 paths.daemon_log(),
81 PathBuf::from("/tmp/stackless-test/daemon.log")
82 );
83 assert_eq!(
84 paths.spawn_lock(),
85 PathBuf::from("/tmp/stackless-test/daemon.spawn.lock")
86 );
87 assert_eq!(
88 paths.logs_dir("demo"),
89 PathBuf::from("/tmp/stackless-test/logs/demo")
90 );
91 assert_eq!(
92 paths.persistence_marker(),
93 PathBuf::from("/tmp/stackless-test/daemon.persistence")
94 );
95 }
96
97 #[test]
98 fn from_env_matches_store_wrappers() {
99 let paths = Paths::from_env();
100 assert_eq!(paths.state_dir(), crate::state::Store::state_dir());
101 assert_eq!(paths.db_path(), crate::state::Store::default_path());
102 }
103}