systemprompt_cloud/paths/
cloud.rs1use 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}