1use std::path::PathBuf;
6
7#[derive(Clone, Debug)]
8pub struct Context {
9 pub(crate) home: PathBuf,
10 pub(crate) pitboard_home: PathBuf,
11 pub(crate) claude_config_dir: Option<String>,
13 pub(crate) secure_storage_dir: Option<String>,
16 pub(crate) user: Option<String>,
18 pub(crate) claude_program: PathBuf,
20 pub(crate) api_base: Option<String>,
22 pub(crate) hover_rest: bool,
24}
25
26impl Context {
27 pub fn new(home: PathBuf) -> Context {
31 Context {
32 pitboard_home: home.join(".pitboard"),
33 home,
34 claude_config_dir: None,
35 secure_storage_dir: None,
36 user: None,
37 claude_program: PathBuf::from("claude"),
38 api_base: None,
39 hover_rest: false,
40 }
41 }
42
43 pub fn with_pitboard_home(mut self, dir: PathBuf) -> Context {
44 self.pitboard_home = dir;
45 self
46 }
47
48 pub fn with_claude_config_dir(mut self, dir: String) -> Context {
50 self.claude_config_dir = Some(dir).filter(|d| !d.is_empty());
51 self
52 }
53
54 pub fn with_secure_storage_dir(mut self, dir: String) -> Context {
57 self.secure_storage_dir = Some(dir);
58 self
59 }
60
61 pub fn with_user(mut self, user: String) -> Context {
63 self.user = Some(user);
64 self
65 }
66
67 pub fn with_claude_program(mut self, program: PathBuf) -> Context {
69 self.claude_program = program;
70 self
71 }
72
73 pub fn from_env() -> Context {
74 let var = |name: &str| std::env::var(name).ok();
75 let home = std::env::var_os("HOME")
76 .map(PathBuf::from)
77 .unwrap_or_default();
78 Context {
79 pitboard_home: std::env::var_os("PITBOARD_HOME")
80 .map(PathBuf::from)
81 .unwrap_or_else(|| home.join(".pitboard")),
82 home,
83 claude_config_dir: var("CLAUDE_CONFIG_DIR").filter(|v| !v.is_empty()),
84 secure_storage_dir: var("CLAUDE_SECURESTORAGE_CONFIG_DIR"),
85 user: var("USER"),
86 claude_program: PathBuf::from("claude"),
87 api_base: var("PITBOARD_API_BASE"),
88 hover_rest: var("CLAUDE_CODE_HOVER_REST").is_some_and(|v| v == "1" || v == "true"),
89 }
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn an_explicit_context_reads_claude_codes_settings_the_way_the_environment_does() {
99 let ctx = Context::new(PathBuf::from("/home/x"))
100 .with_claude_config_dir(String::new())
101 .with_secure_storage_dir(String::new());
102 assert_eq!(ctx.pitboard_home, PathBuf::from("/home/x/.pitboard"));
103 assert_eq!(ctx.claude_config_dir, None, "empty means unset");
104 assert_eq!(
105 ctx.secure_storage_dir.as_deref(),
106 Some(""),
107 "empty is set, and pins the default slot"
108 );
109 assert_eq!(ctx.claude_program, PathBuf::from("claude"));
110 }
111}