1use std::marker::Unpin;
2use std::path::Path;
3use std::path::PathBuf;
4
5use tracing::{debug, info, warn};
6
7use crate::Res;
8use crate::flow;
9use crate::installed_package::InstalledPackage;
10use crate::io::manifest::RowsStream;
11use crate::io::manifest::build_manifest_from_rows_stream;
12use crate::io::remote::Remote;
13use crate::io::remote::RemoteS3;
14use crate::io::storage::LocalStorage;
15use crate::io::storage::Storage;
16use crate::lineage;
17use crate::lineage::DomainLineage;
18use crate::lineage::Home;
19use crate::manifest::Manifest;
20use crate::manifest::ManifestHeader;
21use crate::paths;
22use quilt_uri::ManifestUri;
23use quilt_uri::Namespace;
24
25#[derive(Debug)]
28pub struct LocalDomain<S: Storage = LocalStorage, R: Remote = RemoteS3> {
29 paths: paths::DomainPaths,
30 lineage: lineage::DomainLineageIo,
31 storage: S,
32 remote: R,
33}
34
35impl LocalDomain {
36 pub fn get_remote(&self) -> &RemoteS3 {
37 &self.remote
38 }
39
40 pub fn new(root_dir: impl AsRef<Path>) -> Self {
41 let paths = paths::DomainPaths::new(root_dir.as_ref().to_path_buf());
42 let lineage = lineage::DomainLineageIo::new(paths.lineage());
43 let storage = LocalStorage::new();
44 let remote = RemoteS3::new(paths.clone(), storage.clone());
45 Self {
46 paths,
47 lineage,
48 storage,
49 remote,
50 }
51 }
52
53 pub async fn get_home(&self) -> Res<Home> {
54 let lineage: DomainLineage = self.lineage.read(&self.storage).await?;
55 Ok(lineage.home)
56 }
57
58 pub async fn set_home(&self, dir: impl AsRef<Path>) -> Res<Home> {
59 info!("Setting home directory to {}", dir.as_ref().display());
60 Ok(self.lineage.set_home(&self.storage, dir).await?.home)
61 }
62
63 pub async fn scaffold_paths_for_installing(&self, namespace: &Namespace) -> Res {
64 debug!(
65 "Scaffolding installation paths for namespace: {}",
66 namespace
67 );
68 let home = self.get_home().await?;
69 self.paths
70 .scaffold_for_installing(&self.storage, &home, namespace)
71 .await
72 }
73
74 pub async fn scaffold_paths_for_caching(&self, bucket: &str) -> Res {
75 debug!("Scaffolding cache paths for bucket: {}", bucket);
76 self.paths.scaffold_for_caching(&self.storage, bucket).await
77 }
78
79 pub async fn browse_remote_manifest(&self, uri: &ManifestUri) -> Res<Manifest> {
80 self.scaffold_paths_for_caching(&uri.bucket).await?;
81
82 debug!("Initiating browse flow for manifest {}", uri.hash);
83 flow::browse(&self.paths, &self.storage, &self.remote, uri)
84 .await
85 .inspect(|_| debug!("Successfully browsed manifest {}", uri.hash))
86 .inspect_err(|e| warn!("Failed to browse manifest {}: {}", uri.hash, e))
87 }
88
89 pub fn create_installed_package(&self, namespace: Namespace) -> Res<InstalledPackage> {
90 Ok(InstalledPackage {
92 lineage: self.lineage.create_package_lineage(namespace.clone()),
93 namespace,
94 paths: self.paths.clone(),
95 remote: self.remote.try_clone()?,
96 storage: self.storage.clone(),
97 })
98 }
99
100 pub async fn install_package(&self, manifest_uri: &ManifestUri) -> Res<InstalledPackage> {
101 info!("Installing package: {}", manifest_uri.namespace);
102 debug!("Installing from manifest: {}", manifest_uri.display());
103
104 debug!("Preparing paths for installation");
105 self.scaffold_paths_for_caching(&manifest_uri.bucket)
106 .await?;
107 self.scaffold_paths_for_installing(&manifest_uri.namespace)
108 .await?;
109
110 debug!("Reading domain lineage");
111 let lineage: DomainLineage = self.lineage.read(&self.storage).await?;
112
113 debug!("Executing package installation flow");
114 let lineage = flow::install_package(
115 lineage,
116 &self.paths,
117 &self.storage,
118 &self.remote,
119 manifest_uri,
120 )
121 .await?;
122
123 debug!("Updating domain lineage");
124 self.lineage.write(&self.storage, lineage).await?;
125
126 info!("Successfully installed package: {}", manifest_uri.namespace);
127 self.create_installed_package(manifest_uri.namespace.clone())
128 }
129
130 pub async fn create_package(
131 &self,
132 namespace: Namespace,
133 source: Option<PathBuf>,
134 message: Option<String>,
135 ) -> Res<InstalledPackage> {
136 info!("Creating package: {}", namespace);
137
138 let lineage: DomainLineage = self.lineage.read(&self.storage).await?;
139
140 let lineage = flow::create(
141 lineage,
142 &self.paths,
143 &self.storage,
144 namespace.clone(),
145 source,
146 message,
147 )
148 .await?;
149
150 self.lineage.write(&self.storage, lineage).await?;
151
152 info!("Successfully created package: {}", namespace);
153 self.create_installed_package(namespace)
154 }
155
156 pub async fn uninstall_package(&self, namespace: Namespace) -> Res<()> {
157 info!("Uninstalling package: {}", namespace);
158
159 debug!("Preparing paths for uninstallation");
160 self.scaffold_paths_for_installing(&namespace).await?;
161
162 debug!("Reading current lineage state");
163 let lineage = self.lineage.read(&self.storage).await?;
164
165 debug!("Executing package uninstallation flow");
166 let lineage =
167 flow::uninstall_package(lineage, &self.paths, &self.storage, namespace.clone()).await?;
168
169 debug!("Updating domain lineage after uninstallation");
170 self.lineage.write(&self.storage, lineage).await?;
171
172 info!("Successfully uninstalled package: {}", namespace);
173 Ok(())
174 }
175
176 pub async fn list_installed_packages(&self) -> Res<Vec<InstalledPackage>> {
177 debug!("Listing installed packages");
178 let lineage = self.lineage.read(&self.storage).await?;
179 let namespaces = lineage.namespaces();
180
181 debug!("Found {} installed packages", namespaces.len());
182 let mut packages = Vec::with_capacity(namespaces.len());
183 for namespace in namespaces {
184 packages.push(self.create_installed_package(namespace)?);
185 }
186 Ok(packages)
187 }
188
189 pub async fn get_installed_package(
190 &self,
191 namespace: &Namespace,
192 ) -> Res<Option<InstalledPackage>> {
193 debug!("Looking up installed package: {}", namespace);
194 let lineage = self.lineage.read(&self.storage).await?;
195 if lineage.packages.contains_key(namespace) {
196 debug!("Found installed package: {}", namespace);
197 Ok(Some(self.create_installed_package(namespace.to_owned())?))
198 } else {
199 debug!("Package not found: {}", namespace);
200 Ok(None)
201 }
202 }
203
204 pub async fn build_manifest(
205 &self,
206 dest_path: PathBuf,
207 stream: impl RowsStream + Unpin,
208 ) -> Res<(PathBuf, String)> {
209 info!("Building manifest at: {}", dest_path.display());
210 let dest_dir = dest_path.parent().unwrap_or(&dest_path).to_path_buf();
211 build_manifest_from_rows_stream(&self.storage, dest_dir, ManifestHeader::default(), stream)
212 .await
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 use tempfile::TempDir;
221 use test_log::test;
222
223 #[test(tokio::test)]
224 async fn test_list_installed_packages() -> Res<()> {
225 let temp_dir = TempDir::new()?;
227 let local_domain = super::LocalDomain::new(temp_dir.path());
228
229 local_domain.set_home(&temp_dir.path()).await?;
231
232 let packages = local_domain.list_installed_packages().await?;
234 assert!(packages.is_empty());
235
236 let mut lineage = local_domain.lineage.read(&local_domain.storage).await?;
238
239 let namespaces = vec![
240 Namespace::from(("foo", "bar")),
241 Namespace::from(("test", "package")),
242 Namespace::from(("abc", "xyz")),
243 ];
244
245 for namespace in &namespaces {
246 lineage.packages.insert(
247 namespace.clone(),
248 crate::lineage::PackageLineage {
249 commit: None,
250 remote_uri: Some(ManifestUri {
251 bucket: "test-bucket".to_string(),
252 namespace: namespace.clone(),
253 hash: "abcdef".to_string(),
254 origin: None,
255 }),
256 base_hash: "abcdef".to_string(),
257 latest_hash: "abcdef".to_string(),
258 paths: std::collections::BTreeMap::new(),
259 },
260 );
261 }
262
263 local_domain
264 .lineage
265 .write(&local_domain.storage, lineage)
266 .await?;
267
268 let packages = local_domain.list_installed_packages().await?;
270 assert_eq!(packages.len(), 3);
271
272 assert_eq!(packages[0].namespace, Namespace::from(("abc", "xyz")));
274 assert_eq!(packages[1].namespace, Namespace::from(("foo", "bar")));
275 assert_eq!(packages[2].namespace, Namespace::from(("test", "package")));
276
277 Ok(())
278 }
279}