1use std::path::Path;
5use std::path::PathBuf;
6
7#[cfg(test)]
8use tempfile::TempDir;
9use tracing::error;
10
11use crate::Res;
12use crate::io::storage::Storage;
13use crate::lineage::Home;
14use quilt_uri::Host;
15use quilt_uri::ManifestUri;
16use quilt_uri::Namespace;
17
18pub const AUTH_CLIENT: &str = "client.json";
19pub const AUTH_CREDENTIALS: &str = "credentials.json";
20pub const AUTH_DIR: &str = ".auth";
21pub const AUTH_TOKENS: &str = "tokens.json";
22
23pub fn list_auth_hosts(data_dir: &Path) -> Vec<String> {
28 let auth_dir = data_dir.join(AUTH_DIR);
29 let mut hosts: Vec<String> = Vec::new();
30 if auth_dir.exists()
31 && let Ok(entries) = std::fs::read_dir(&auth_dir)
32 {
33 for entry in entries.flatten() {
34 if entry.file_type().is_ok_and(|t| t.is_dir())
35 && let Some(name) = entry.file_name().to_str()
36 {
37 hosts.push(name.to_string());
38 }
39 }
40 }
41 hosts.sort();
42 hosts
43}
44
45const LINEAGE_FILE: &str = ".quilt/data.json";
46
47const INSTALLED_DIR: &str = ".quilt/installed";
48const MANIFEST_DIR: &str = ".quilt/packages";
52const OBJECTS_DIR: &str = ".quilt/objects";
53
54pub use quilt_uri::paths::get_manifest_key;
55pub use quilt_uri::paths::tag_key;
56
57pub fn package_home(home: &Home, namespace: &Namespace) -> PathBuf {
59 home.join(namespace.to_string())
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Default)]
66pub struct DomainPaths {
67 root_dir: PathBuf,
68}
69
70impl DomainPaths {
71 pub fn new(root_dir: PathBuf) -> Self {
72 DomainPaths { root_dir }
73 }
74
75 pub fn auth_host(&self, host: &Host) -> PathBuf {
76 self.root_dir
77 .join(AUTH_DIR)
78 .join(PathBuf::from(host.to_string()))
79 }
80
81 pub fn installed_manifest(&self, namespace: &Namespace, hash: &str) -> PathBuf {
90 self.installed_manifests_dir(namespace).join(hash)
91 }
92
93 pub fn installed_manifests_dir(&self, namespace: &Namespace) -> PathBuf {
95 self.root_dir
96 .join(INSTALLED_DIR)
97 .join(namespace.to_string())
98 }
99
100 pub fn lineage(&self) -> PathBuf {
102 self.root_dir.join(LINEAGE_FILE)
103 }
104
105 pub fn cached_manifest(&self, uri: &ManifestUri) -> PathBuf {
107 self.root_dir
108 .join(MANIFEST_DIR)
109 .join(&uri.bucket)
110 .join(&uri.hash)
111 }
112
113 pub fn cached_manifests_dir(&self, bucket: &str) -> PathBuf {
115 self.root_dir.join(MANIFEST_DIR).join(bucket)
116 }
117
118 pub fn objects_dir(&self) -> PathBuf {
120 self.root_dir.join(OBJECTS_DIR)
121 }
122
123 pub fn object(&self, hash: &[u8]) -> PathBuf {
125 self.objects_dir().join(hex::encode(hash))
126 }
127
128 fn required(&self) -> Vec<PathBuf> {
130 vec![
131 self.root_dir.join(INSTALLED_DIR),
132 self.objects_dir(),
133 self.root_dir.join(MANIFEST_DIR),
134 ]
135 }
136
137 fn required_for_installing(&self, home: &Home, namespace: &Namespace) -> Res<Vec<PathBuf>> {
139 let mut paths = vec![];
140 paths.extend(self.required());
141 paths.extend(vec![
142 package_home(home, namespace),
143 self.installed_manifests_dir(namespace),
144 ]);
145 Ok(paths)
146 }
147
148 pub async fn scaffold_for_installing(
149 &self,
150 storage: &impl Storage,
151 home: &Home,
152 namespace: &Namespace,
153 ) -> Res {
154 scaffold_paths(storage, self.required_for_installing(home, namespace)?).await
155 }
156
157 fn required_for_caching(&self, bucket: &str) -> Vec<PathBuf> {
159 let mut paths = vec![];
160 paths.extend(self.required());
161 paths.extend(vec![self.cached_manifests_dir(bucket)]);
162 paths
163 }
164
165 pub async fn scaffold_for_caching(&self, storage: &impl Storage, bucket: &str) -> Res {
166 scaffold_paths(storage, self.required_for_caching(bucket)).await
167 }
168
169 #[cfg(test)]
170 pub fn from_temp_dir() -> Res<(Self, TempDir)> {
171 let temp_dir = TempDir::new()?;
172 Ok((DomainPaths::new(temp_dir.path().to_path_buf()), temp_dir))
173 }
174}
175
176pub async fn copy_cached_to_installed(
177 paths: &DomainPaths,
178 storage: &impl Storage,
179 manifest_uri: &ManifestUri,
180) -> Res {
181 match storage
182 .copy(
183 paths.cached_manifest(manifest_uri),
184 paths.installed_manifest(&manifest_uri.namespace, &manifest_uri.hash),
185 )
186 .await
187 {
188 Ok(_) => Ok(()),
189 Err(e) => {
190 error!(
191 "Failed to copy cached manifest to installed location for manifest_uri {}: {}",
192 manifest_uri, e
193 );
194 Err(e)
195 }
196 }
197}
198
199async fn scaffold_paths(storage: &impl Storage, paths: Vec<PathBuf>) -> Res {
201 for path in paths {
202 storage.create_dir_all(&path).await?;
203 }
204 Ok(())
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 use test_log::test;
212
213 #[test]
214 fn test_required_paths() {
215 let paths = DomainPaths::new(PathBuf::from("foo/bar"));
216 let scaffolded_paths = paths.required();
217 assert_eq!(
218 scaffolded_paths,
219 vec![
220 PathBuf::from("foo/bar/.quilt/installed"),
221 PathBuf::from("foo/bar/.quilt/objects"),
222 PathBuf::from("foo/bar/.quilt/packages"),
223 ]
224 );
225 }
226
227 #[test]
228 fn test_package_home() -> Res {
229 let home = Home::from("/home/user/quilt");
230 let namespace = Namespace::from(("test", "package"));
231
232 let pkg_home = package_home(&home, &namespace);
233 assert_eq!(pkg_home, PathBuf::from("/home/user/quilt/test/package"));
234
235 Ok(())
236 }
237}