lux_lib/remote_package_db/
mod.rs1use crate::{
2 config::{Config, ConfigError},
3 lockfile::{LocalPackageLock, LockfileIntegrityError},
4 manifest::{Manifest, ManifestError},
5 package::{
6 PackageName, PackageReq, PackageSpec, PackageVersion, RemotePackage,
7 RemotePackageTypeFilterSpec,
8 },
9 progress::{Progress, ProgressBar},
10};
11use itertools::Itertools;
12
13use miette::Diagnostic;
14use thiserror::Error;
15#[derive(Clone, Debug)]
17pub struct RemotePackageDB(Impl);
18
19#[derive(Clone, Debug)]
20enum Impl {
21 LuarocksManifests(Vec<Manifest>),
22 Lock(LocalPackageLock),
23}
24
25#[derive(Error, Debug, Diagnostic)]
26pub enum RemotePackageDBError {
27 #[error(transparent)]
28 #[diagnostic(transparent)]
29 ManifestError(#[from] ManifestError),
30 #[error(transparent)]
31 #[diagnostic(transparent)]
32 ConfigError(#[from] ConfigError),
33}
34
35#[derive(Error, Debug, Diagnostic)]
36pub enum SearchError {
37 #[error("no rock that matches '{0}' found")]
38 RockNotFound(PackageReq),
39 #[error("no rock that matches '{0}' found in the lockfile.")]
40 RockNotFoundInLockfile(PackageReq),
41 #[error("error when pulling manifest:\n{0}")]
42 #[diagnostic(forward(0))]
43 Manifest(#[from] ManifestError),
44}
45
46#[derive(Error, Debug, Diagnostic)]
47pub enum RemotePackageDbIntegrityError {
48 #[error(transparent)]
49 #[diagnostic(transparent)]
50 Lockfile(#[from] LockfileIntegrityError),
51}
52
53impl RemotePackageDB {
54 pub async fn from_config(
55 config: &Config,
56 progress: &Progress<ProgressBar>,
57 ) -> Result<Self, RemotePackageDBError> {
58 let mut manifests = Vec::new();
59 for server in config.enabled_dev_servers()? {
60 let manifest = Manifest::from_config(server, config, progress).await?;
61 manifests.push(manifest);
62 }
63 for server in config.extra_servers() {
64 let manifest = Manifest::from_config(server.clone(), config, progress).await?;
65 manifests.push(manifest);
66 }
67 manifests.push(Manifest::from_config(config.server().clone(), config, progress).await?);
68 Ok(Self(Impl::LuarocksManifests(manifests)))
69 }
70
71 pub(crate) fn find(
73 &self,
74 package_req: &PackageReq,
75 filter: Option<RemotePackageTypeFilterSpec>,
76 progress: &Progress<ProgressBar>,
77 ) -> Result<RemotePackage, SearchError> {
78 match &self.0 {
79 Impl::LuarocksManifests(manifests) => match manifests.iter().find_map(|manifest| {
80 progress.map(|p| p.set_message(format!("🔎 Searching {}", &manifest.server_url())));
81 manifest.find(package_req, filter.clone())
82 }) {
83 Some(package) => Ok(package),
84 None => Err(SearchError::RockNotFound(package_req.clone())),
85 },
86 Impl::Lock(lockfile) => {
87 match lockfile.has_rock(package_req, filter).map(|local_package| {
88 RemotePackage::new(
89 PackageSpec::new(local_package.spec.name, local_package.spec.version),
90 local_package.source,
91 local_package.source_url,
92 )
93 }) {
94 Some(package) => Ok(package),
95 None => Err(SearchError::RockNotFoundInLockfile(package_req.clone())),
96 }
97 }
98 }
99 }
100
101 pub fn search(&self, package_req: &PackageReq) -> Vec<(&PackageName, Vec<&PackageVersion>)> {
103 match &self.0 {
104 Impl::LuarocksManifests(manifests) => manifests
105 .iter()
106 .flat_map(|manifest| {
107 manifest
108 .metadata()
109 .repository
110 .iter()
111 .filter_map(|(name, elements)| {
112 if name.to_string().contains(&package_req.name().to_string()) {
113 Some((
114 name,
115 elements
116 .keys()
117 .filter(|version| {
118 package_req.version_req().matches(version)
119 })
120 .sorted_by(|a, b| Ord::cmp(b, a))
121 .collect_vec(),
122 ))
123 } else {
124 None
125 }
126 })
127 })
128 .collect(),
129 Impl::Lock(lockfile) => lockfile
130 .rocks()
131 .values()
132 .filter_map(|package| {
133 let name = package.name();
136 if name.to_string().contains(&package_req.name().to_string()) {
137 Some((name, vec![package.version()]))
138 } else {
139 None
140 }
141 })
142 .collect_vec(),
143 }
144 }
145
146 pub(crate) fn latest_version(&self, rock_name: &PackageName) -> Option<PackageVersion> {
148 self.latest_match(&rock_name.clone().into(), None)
149 .map(|result| result.version().clone())
150 }
151
152 pub fn latest_match(
154 &self,
155 package_req: &PackageReq,
156 filter: Option<RemotePackageTypeFilterSpec>,
157 ) -> Option<PackageSpec> {
158 match self.find(package_req, filter, &Progress::no_progress()) {
159 Ok(result) => Some(result.package),
160 Err(_) => None,
161 }
162 }
163}
164
165impl From<Manifest> for RemotePackageDB {
166 fn from(manifest: Manifest) -> Self {
167 Self(Impl::LuarocksManifests(vec![manifest]))
168 }
169}
170
171impl From<LocalPackageLock> for RemotePackageDB {
172 fn from(lock: LocalPackageLock) -> Self {
173 Self(Impl::Lock(lock))
174 }
175}