objectiveai_cli/filesystem/
client.rs1use std::path::PathBuf;
2
3#[derive(Debug, Clone)]
24pub struct Client {
25 dir: PathBuf,
26 state: String,
27 pub commit_author_name: String,
28 pub commit_author_email: String,
29}
30
31impl Client {
32 pub fn new(
41 dir: Option<impl Into<PathBuf>>,
42 state: Option<impl Into<String>>,
43 commit_author_name: Option<impl Into<String>>,
44 commit_author_email: Option<impl Into<String>>,
45 ) -> Self {
46 let dir = match dir {
47 Some(dir) => dir.into(),
48 None => {
49 #[cfg(feature = "env")]
50 let env_dir = std::env::var("OBJECTIVEAI_DIR").ok();
51 #[cfg(not(feature = "env"))]
52 let env_dir: Option<String> = None;
53 match env_dir {
54 Some(dir) => PathBuf::from(dir),
55 None => dirs::home_dir()
56 .unwrap_or_else(|| PathBuf::from("."))
57 .join(".objectiveai"),
58 }
59 }
60 };
61 let state = match state {
62 Some(state) => state.into(),
63 None => {
64 #[cfg(feature = "env")]
65 let env_state = std::env::var("OBJECTIVEAI_STATE").ok();
66 #[cfg(not(feature = "env"))]
67 let env_state: Option<String> = None;
68 env_state.unwrap_or_else(|| "default".to_string())
69 }
70 };
71 assert!(
72 is_valid_state_name(&state),
73 "OBJECTIVEAI_STATE {state:?} is invalid: state names must match [A-Za-z0-9_-]+",
74 );
75 Self {
76 dir,
77 state,
78 commit_author_name: resolve_author_name(commit_author_name),
79 commit_author_email: resolve_author_email(commit_author_email),
80 }
81 }
82
83 pub fn dir(&self) -> &PathBuf {
87 &self.dir
88 }
89
90 pub fn state(&self) -> &str {
92 &self.state
93 }
94
95 pub fn bin_dir(&self) -> PathBuf {
98 self.dir.join("bin")
99 }
100
101 pub fn state_dir(&self) -> PathBuf {
104 self.dir.join("state").join(&self.state)
105 }
106
107 pub fn config_path(&self) -> PathBuf {
109 self.state_dir().join("config.json")
110 }
111
112 pub fn global_config_path(&self) -> PathBuf {
115 self.bin_dir().join("config.json")
116 }
117
118 pub fn logs_dir(&self) -> PathBuf {
119 self.state_dir().join("logs")
120 }
121}
122
123fn is_valid_state_name(state: &str) -> bool {
125 !state.is_empty()
126 && state
127 .chars()
128 .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
129}
130
131fn resolve_author_name(explicit: Option<impl Into<String>>) -> String {
132 if let Some(name) = explicit {
133 return name.into();
134 }
135 #[cfg(feature = "env")]
136 if let Ok(name) = std::env::var("COMMIT_AUTHOR_NAME") {
137 return name;
138 }
139 "ObjectiveAI".to_string()
140}
141
142fn resolve_author_email(explicit: Option<impl Into<String>>) -> String {
143 if let Some(email) = explicit {
144 return email.into();
145 }
146 #[cfg(feature = "env")]
147 if let Ok(email) = std::env::var("COMMIT_AUTHOR_EMAIL") {
148 return email;
149 }
150 "admin@objectiveai.dev".to_string()
151}