Skip to main content

loadsmith_registry/registries/
offline.rs

1use std::{collections::HashMap, pin::Pin};
2
3use loadsmith_core::{Checksum, Dependency, FileUrl, PackageId, PackageRef, Version};
4use serde::{Deserialize, Serialize};
5
6use crate::{Error, Registry, ResolvedVersion, Result, VersionInfo};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Package {
10    pub id: PackageId,
11    pub versions: Vec<PackageVersion>,
12}
13
14impl Package {
15    pub fn new(id: impl Into<PackageId>, versions: Vec<PackageVersion>) -> Self {
16        Self {
17            id: id.into(),
18            versions,
19        }
20    }
21
22    fn version_by_version<'a>(&'a self, version: &Version) -> Result<&'a PackageVersion> {
23        self.versions
24            .iter()
25            .find(|v| v.version == *version)
26            .ok_or(Error::VersionNotFound)
27    }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct PackageVersion {
32    pub version: Version,
33    pub url: FileUrl,
34    #[serde(default)]
35    pub size: Option<u64>,
36    #[serde(default)]
37    pub checksum: Option<Checksum>,
38    pub deps: Vec<Dependency>,
39}
40
41impl PackageVersion {
42    pub fn new(version: impl Into<Version>, download_url: impl Into<FileUrl>) -> Self {
43        Self {
44            version: version.into(),
45            url: download_url.into(),
46            size: None,
47            checksum: None,
48            deps: Vec::new(),
49        }
50    }
51
52    pub fn with_size(mut self, size: u64) -> Self {
53        self.size = Some(size);
54        self
55    }
56
57    pub fn with_checksum(mut self, checksum: impl Into<Checksum>) -> Self {
58        self.checksum = Some(checksum.into());
59        self
60    }
61
62    pub fn with_deps(mut self, deps: Vec<Dependency>) -> Self {
63        self.deps = deps;
64        self
65    }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, Default)]
69pub struct OfflineRegistry {
70    packages: HashMap<PackageId, Package>,
71}
72
73impl OfflineRegistry {
74    pub fn new(packages: HashMap<PackageId, Package>) -> Self {
75        Self { packages }
76    }
77
78    fn package_by_id<'a>(&'a self, id: &PackageId) -> Result<&'a Package> {
79        self.packages.get(id).ok_or(Error::PackageNotFound)
80    }
81}
82
83impl Registry for OfflineRegistry {
84    fn version_info<'a>(
85        &'a self,
86        id: &'a PackageId,
87        _metadata: Option<&'a serde_json::Value>,
88    ) -> Pin<Box<dyn Future<Output = Result<Vec<VersionInfo>>> + 'a>> {
89        Box::pin(async move {
90            let package = self.package_by_id(id)?;
91
92            let versions = package
93                .versions
94                .iter()
95                .map(|v| VersionInfo {
96                    version: v.version.clone(),
97                })
98                .collect();
99
100            Ok(versions)
101        })
102    }
103
104    fn resolve<'a>(
105        &'a self,
106        ref_: &'a PackageRef,
107        _metadata: Option<&'a serde_json::Value>,
108    ) -> Pin<Box<dyn Future<Output = Result<ResolvedVersion>> + 'a>> {
109        Box::pin(async move {
110            let version = self
111                .package_by_id(ref_.id())?
112                .version_by_version(ref_.version())?;
113
114            Ok(ResolvedVersion {
115                url: version.url.clone(),
116                size: version.size,
117                checksum: version.checksum.clone(),
118                deps: version.deps.clone(),
119            })
120        })
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    use std::assert_matches;
129
130    #[tokio::test]
131    async fn it_works() {
132        let registry = OfflineRegistry::new(HashMap::from_iter([(
133            PackageId::new("author-name"),
134            Package::new(
135                PackageId::new("author-name"),
136                vec![
137                    PackageVersion::new(
138                        Version::new(1, 0, 0),
139                        FileUrl::try_from_url("https://example.com/package-1.0.0.zip").unwrap(),
140                    ),
141                    PackageVersion::new(
142                        Version::new(1, 1, 0),
143                        FileUrl::try_from_url("https://example.com/package-1.1.0.zip").unwrap(),
144                    ),
145                ],
146            ),
147        )]));
148
149        let versions = registry
150            .version_info(&PackageId::new("author-name"), None)
151            .await
152            .unwrap();
153
154        assert_eq!(versions.len(), 2);
155
156        let non_existent_package = registry
157            .version_info(&PackageId::new("non-existent"), None)
158            .await;
159
160        assert_matches!(non_existent_package, Err(Error::PackageNotFound));
161    }
162}