Skip to main content

systemprompt_cloud/paths/
cloud.rs

1use std::path::{Path, PathBuf};
2
3use crate::constants::{cli_session, credentials, dir_names, tenants};
4
5use super::{resolve_path, ProjectContext};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum CloudPath {
9    Credentials,
10    Tenants,
11    CliSession,
12    SessionsDir,
13}
14
15impl CloudPath {
16    #[must_use]
17    pub const fn default_filename(&self) -> &'static str {
18        match self {
19            Self::Credentials => credentials::DEFAULT_FILE_NAME,
20            Self::Tenants => tenants::DEFAULT_FILE_NAME,
21            Self::CliSession => cli_session::DEFAULT_FILE_NAME,
22            Self::SessionsDir => dir_names::SESSIONS,
23        }
24    }
25
26    #[must_use]
27    pub const fn default_dirname(&self) -> &'static str {
28        match self {
29            Self::Credentials => credentials::DEFAULT_DIR_NAME,
30            Self::Tenants => tenants::DEFAULT_DIR_NAME,
31            Self::CliSession | Self::SessionsDir => cli_session::DEFAULT_DIR_NAME,
32        }
33    }
34
35    #[must_use]
36    pub const fn is_dir(&self) -> bool {
37        matches!(self, Self::SessionsDir)
38    }
39}
40
41#[derive(Debug, Clone)]
42pub struct CloudPaths {
43    base_dir: PathBuf,
44    credentials_path: Option<PathBuf>,
45    tenants_path: Option<PathBuf>,
46}
47
48impl CloudPaths {
49    #[must_use]
50    pub fn new(profile_dir: &Path) -> Self {
51        Self {
52            base_dir: profile_dir.join(credentials::DEFAULT_DIR_NAME),
53            credentials_path: None,
54            tenants_path: None,
55        }
56    }
57
58    #[must_use]
59    pub fn from_project_context(ctx: &ProjectContext) -> Self {
60        Self {
61            base_dir: ctx.systemprompt_dir(),
62            credentials_path: Some(ctx.local_credentials()),
63            tenants_path: Some(ctx.local_tenants()),
64        }
65    }
66
67    #[must_use]
68    pub fn from_config(
69        profile_dir: &Path,
70        credentials_path_str: &str,
71        tenants_path_str: &str,
72    ) -> Self {
73        let credentials_path = if credentials_path_str.is_empty() {
74            None
75        } else {
76            Some(resolve_path(profile_dir, credentials_path_str))
77        };
78
79        let tenants_path = if tenants_path_str.is_empty() {
80            None
81        } else {
82            Some(resolve_path(profile_dir, tenants_path_str))
83        };
84
85        let base_dir = credentials_path
86            .as_ref()
87            .and_then(|p| p.parent())
88            .map(PathBuf::from)
89            .unwrap_or_else(|| {
90                profile_dir
91                    .ancestors()
92                    .find(|p| p.file_name().is_some_and(|n| n == ".systemprompt"))
93                    .map(PathBuf::from)
94                    .unwrap_or_else(|| profile_dir.join(credentials::DEFAULT_DIR_NAME))
95            });
96
97        Self {
98            base_dir,
99            credentials_path,
100            tenants_path,
101        }
102    }
103
104    #[must_use]
105    pub fn resolve(&self, path: CloudPath) -> PathBuf {
106        match path {
107            CloudPath::Credentials => self
108                .credentials_path
109                .clone()
110                .unwrap_or_else(|| self.base_dir.join(credentials::DEFAULT_FILE_NAME)),
111            CloudPath::Tenants => self
112                .tenants_path
113                .clone()
114                .unwrap_or_else(|| self.base_dir.join(tenants::DEFAULT_FILE_NAME)),
115            CloudPath::CliSession => self.base_dir.join(cli_session::DEFAULT_FILE_NAME),
116            CloudPath::SessionsDir => self.base_dir.join(dir_names::SESSIONS),
117        }
118    }
119
120    #[must_use]
121    pub fn base_dir(&self) -> &Path {
122        &self.base_dir
123    }
124
125    #[must_use]
126    pub fn exists(&self, path: CloudPath) -> bool {
127        self.resolve(path).exists()
128    }
129}
130
131#[allow(clippy::unnecessary_wraps)]
132pub fn get_cloud_paths() -> anyhow::Result<CloudPaths> {
133    let project_ctx = ProjectContext::discover();
134    Ok(CloudPaths::from_project_context(&project_ctx))
135}