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};
10use itertools::Itertools;
11
12use miette::Diagnostic;
13use thiserror::Error;
14use tracing::span;
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)]
36#[non_exhaustive]
37pub enum SearchError {
38 #[error("no rock that matches '{0}' found")]
39 RockNotFound(PackageReq),
40 #[error("no rock that matches '{0}' found in the lockfile.")]
41 RockNotFoundInLockfile(PackageReq),
42 #[error("error when pulling manifest:\n{0}")]
43 #[diagnostic(forward(0))]
44 Manifest(#[from] ManifestError),
45}
46
47#[derive(Error, Debug, Diagnostic)]
48#[non_exhaustive]
49pub enum RemotePackageDbIntegrityError {
50 #[error(transparent)]
51 #[diagnostic(transparent)]
52 Lockfile(#[from] LockfileIntegrityError),
53}
54
55impl RemotePackageDB {
56 #[tracing::instrument(level = "trace", skip_all)]
57 pub async fn from_config(config: &Config) -> 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).await?;
61 manifests.push(manifest);
62 }
63 for server in config.extra_servers() {
64 let manifest = Manifest::from_config(server.clone(), config).await?;
65 manifests.push(manifest);
66 }
67 manifests.push(Manifest::from_config(config.server().clone(), config).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 ) -> Result<RemotePackage, SearchError> {
77 let span = span!(
78 tracing::Level::INFO,
79 "🔎 Searching",
80 package = package_req.to_string(),
81 );
82 let _enter = span.enter();
83 match &self.0 {
84 Impl::LuarocksManifests(manifests) => {
85 match manifests
86 .iter()
87 .find_map(|manifest| manifest.find(package_req, filter.clone()))
88 {
89 Some(package) => Ok(package),
90 None => Err(SearchError::RockNotFound(package_req.clone())),
91 }
92 }
93 Impl::Lock(lockfile) => {
94 match lockfile.has_rock(package_req, filter).map(|local_package| {
95 RemotePackage::new(
96 PackageSpec::new(local_package.spec.name, local_package.spec.version),
97 local_package.source,
98 local_package.source_url,
99 )
100 }) {
101 Some(package) => Ok(package),
102 None => Err(SearchError::RockNotFoundInLockfile(package_req.clone())),
103 }
104 }
105 }
106 }
107
108 pub fn search(&self, package_req: &PackageReq) -> Vec<(&PackageName, Vec<&PackageVersion>)> {
110 match &self.0 {
111 Impl::LuarocksManifests(manifests) => manifests
112 .iter()
113 .flat_map(|manifest| {
114 manifest
115 .metadata()
116 .repository
117 .iter()
118 .filter_map(|(name, elements)| {
119 if name.to_string().contains(&package_req.name().to_string()) {
120 Some((
121 name,
122 elements
123 .keys()
124 .filter(|version| {
125 package_req.version_req().matches(version)
126 })
127 .sorted_by(|a, b| Ord::cmp(b, a))
128 .collect_vec(),
129 ))
130 } else {
131 None
132 }
133 })
134 })
135 .collect(),
136 Impl::Lock(lockfile) => lockfile
137 .rocks()
138 .values()
139 .filter_map(|package| {
140 let name = package.name();
143 if name.to_string().contains(&package_req.name().to_string()) {
144 Some((name, vec![package.version()]))
145 } else {
146 None
147 }
148 })
149 .collect_vec(),
150 }
151 }
152
153 pub(crate) fn latest_version(&self, rock_name: &PackageName) -> Option<PackageVersion> {
155 self.latest_match(&rock_name.clone().into(), None)
156 .map(|result| result.version().clone())
157 }
158
159 pub fn latest_match(
161 &self,
162 package_req: &PackageReq,
163 filter: Option<RemotePackageTypeFilterSpec>,
164 ) -> Option<PackageSpec> {
165 match self.find(package_req, filter) {
166 Ok(result) => Some(result.package),
167 Err(_) => None,
168 }
169 }
170}
171
172impl From<Manifest> for RemotePackageDB {
173 fn from(manifest: Manifest) -> Self {
174 Self(Impl::LuarocksManifests(vec![manifest]))
175 }
176}
177
178impl From<LocalPackageLock> for RemotePackageDB {
179 fn from(lock: LocalPackageLock) -> Self {
180 Self(Impl::Lock(lock))
181 }
182}