Skip to main content

systemprompt_cloud/paths/
discovery.rs

1//! Project discovery for cloud deploys (finding the deployable root).
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use std::path::{Path, PathBuf};
7
8use crate::constants::{dir_names, file_names};
9
10#[derive(Debug, Clone)]
11pub struct DiscoveredProject {
12    root: PathBuf,
13    systemprompt_dir: PathBuf,
14}
15
16impl DiscoveredProject {
17    #[must_use]
18    pub fn discover() -> Option<Self> {
19        let cwd = match std::env::current_dir() {
20            Ok(dir) => dir,
21            Err(e) => {
22                tracing::debug!(error = %e, "Failed to get current directory for project discovery");
23                return None;
24            },
25        };
26        Self::discover_from(&cwd)
27    }
28
29    #[must_use]
30    pub fn discover_from(start: &Path) -> Option<Self> {
31        let mut current = start.to_path_buf();
32        loop {
33            let systemprompt_dir = current.join(dir_names::SYSTEMPROMPT);
34            if systemprompt_dir.is_dir() {
35                return Some(Self {
36                    root: current,
37                    systemprompt_dir,
38                });
39            }
40            if !current.pop() {
41                break;
42            }
43        }
44        None
45    }
46
47    #[must_use]
48    pub fn from_root(root: PathBuf) -> Self {
49        let systemprompt_dir = root.join(dir_names::SYSTEMPROMPT);
50        Self {
51            root,
52            systemprompt_dir,
53        }
54    }
55
56    #[must_use]
57    pub fn root(&self) -> &Path {
58        &self.root
59    }
60
61    #[must_use]
62    pub fn systemprompt_dir(&self) -> &Path {
63        &self.systemprompt_dir
64    }
65
66    #[must_use]
67    pub fn credentials_path(&self) -> PathBuf {
68        self.systemprompt_dir.join(file_names::CREDENTIALS)
69    }
70
71    #[must_use]
72    pub fn tenants_path(&self) -> PathBuf {
73        self.systemprompt_dir.join(file_names::TENANTS)
74    }
75
76    #[must_use]
77    pub fn session_path(&self) -> PathBuf {
78        self.systemprompt_dir.join(file_names::SESSION)
79    }
80
81    #[must_use]
82    pub fn sessions_dir(&self) -> PathBuf {
83        self.systemprompt_dir.join(dir_names::SESSIONS)
84    }
85
86    #[must_use]
87    pub fn profiles_dir(&self) -> PathBuf {
88        self.systemprompt_dir.join(dir_names::PROFILES)
89    }
90
91    #[must_use]
92    pub fn profile_dir(&self, name: &str) -> PathBuf {
93        self.profiles_dir().join(name)
94    }
95
96    #[must_use]
97    pub fn profile_config(&self, name: &str) -> PathBuf {
98        self.profile_dir(name).join(file_names::PROFILE_CONFIG)
99    }
100
101    #[must_use]
102    pub fn profile_secrets(&self, name: &str) -> PathBuf {
103        self.profile_dir(name).join(file_names::PROFILE_SECRETS)
104    }
105
106    #[must_use]
107    pub fn docker_dir(&self) -> PathBuf {
108        self.systemprompt_dir.join(dir_names::DOCKER)
109    }
110
111    #[must_use]
112    pub fn storage_dir(&self) -> PathBuf {
113        self.systemprompt_dir.join(dir_names::STORAGE)
114    }
115
116    #[must_use]
117    pub fn is_initialized(&self) -> bool {
118        self.systemprompt_dir.is_dir()
119    }
120
121    #[must_use]
122    pub fn has_credentials(&self) -> bool {
123        self.credentials_path().exists()
124    }
125
126    #[must_use]
127    pub fn has_tenants(&self) -> bool {
128        self.tenants_path().exists()
129    }
130
131    #[must_use]
132    pub fn has_session(&self) -> bool {
133        self.session_path().exists()
134    }
135
136    #[must_use]
137    pub fn has_profile(&self, name: &str) -> bool {
138        self.profile_dir(name).is_dir()
139    }
140}