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)]
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(config: &Config) -> Result<Self, RemotePackageDBError> {
55 let mut manifests = Vec::new();
56 for server in config.enabled_dev_servers()? {
57 let manifest = Manifest::from_config(server, config).await?;
58 manifests.push(manifest);
59 }
60 for server in config.extra_servers() {
61 let manifest = Manifest::from_config(server.clone(), config).await?;
62 manifests.push(manifest);
63 }
64 manifests.push(Manifest::from_config(config.server().clone(), config).await?);
65 Ok(Self(Impl::LuarocksManifests(manifests)))
66 }
67
68 pub(crate) fn find(
70 &self,
71 package_req: &PackageReq,
72 filter: Option<RemotePackageTypeFilterSpec>,
73 ) -> Result<RemotePackage, SearchError> {
74 let span = span!(
75 tracing::Level::INFO,
76 "🔎 Searching",
77 package = package_req.to_string(),
78 );
79 let _enter = span.enter();
80 match &self.0 {
81 Impl::LuarocksManifests(manifests) => {
82 match manifests
83 .iter()
84 .find_map(|manifest| manifest.find(package_req, filter.clone()))
85 {
86 Some(package) => Ok(package),
87 None => Err(SearchError::RockNotFound(package_req.clone())),
88 }
89 }
90 Impl::Lock(lockfile) => {
91 match lockfile.has_rock(package_req, filter).map(|local_package| {
92 RemotePackage::new(
93 PackageSpec::new(local_package.spec.name, local_package.spec.version),
94 local_package.source,
95 local_package.source_url,
96 )
97 }) {
98 Some(package) => Ok(package),
99 None => Err(SearchError::RockNotFoundInLockfile(package_req.clone())),
100 }
101 }
102 }
103 }
104
105 pub fn search(&self, package_req: &PackageReq) -> Vec<(&PackageName, Vec<&PackageVersion>)> {
107 match &self.0 {
108 Impl::LuarocksManifests(manifests) => manifests
109 .iter()
110 .flat_map(|manifest| {
111 manifest
112 .metadata()
113 .repository
114 .iter()
115 .filter_map(|(name, elements)| {
116 if name.to_string().contains(&package_req.name().to_string()) {
117 Some((
118 name,
119 elements
120 .keys()
121 .filter(|version| {
122 package_req.version_req().matches(version)
123 })
124 .sorted_by(|a, b| Ord::cmp(b, a))
125 .collect_vec(),
126 ))
127 } else {
128 None
129 }
130 })
131 })
132 .collect(),
133 Impl::Lock(lockfile) => lockfile
134 .rocks()
135 .values()
136 .filter_map(|package| {
137 let name = package.name();
140 if name.to_string().contains(&package_req.name().to_string()) {
141 Some((name, vec![package.version()]))
142 } else {
143 None
144 }
145 })
146 .collect_vec(),
147 }
148 }
149
150 pub(crate) fn latest_version(&self, rock_name: &PackageName) -> Option<PackageVersion> {
152 self.latest_match(&rock_name.clone().into(), None)
153 .map(|result| result.version().clone())
154 }
155
156 pub fn latest_match(
158 &self,
159 package_req: &PackageReq,
160 filter: Option<RemotePackageTypeFilterSpec>,
161 ) -> Option<PackageSpec> {
162 match self.find(package_req, filter) {
163 Ok(result) => Some(result.package),
164 Err(_) => None,
165 }
166 }
167}
168
169impl From<Manifest> for RemotePackageDB {
170 fn from(manifest: Manifest) -> Self {
171 Self(Impl::LuarocksManifests(vec![manifest]))
172 }
173}
174
175impl From<LocalPackageLock> for RemotePackageDB {
176 fn from(lock: LocalPackageLock) -> Self {
177 Self(Impl::Lock(lock))
178 }
179}