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;
14
15#[derive(Clone, Debug)]
17pub struct RemotePackageDB(Impl);
18
19#[derive(Clone, Debug)]
20enum Impl {
21 LuarocksManifests(Vec<Manifest>),
22 LocalPackageLocks(Vec<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")]
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 #[tracing::instrument(
73 name = "Searching tree-sitter parser",
74 level = "info",
75 skip_all,
76 fields(package = package_req.to_string()),
77 )]
78 pub(crate) fn find(
79 &self,
80 package_req: &PackageReq,
81 filter: Option<RemotePackageTypeFilterSpec>,
82 ) -> Result<RemotePackage, SearchError> {
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::LocalPackageLocks(locks) => {
94 match locks
95 .iter()
96 .filter_map(|lock| lock.has_rock(package_req, filter.clone()))
97 .map(|local_package| {
98 RemotePackage::new(
99 PackageSpec::new(local_package.spec.name, local_package.spec.version),
100 local_package.source,
101 local_package.source_url,
102 )
103 })
104 .next()
105 {
106 Some(package) => Ok(package),
107 None => Err(SearchError::RockNotFoundInLockfile(package_req.clone())),
108 }
109 }
110 }
111 }
112
113 pub fn search(&self, package_req: &PackageReq) -> Vec<(&PackageName, Vec<&PackageVersion>)> {
115 match &self.0 {
116 Impl::LuarocksManifests(manifests) => manifests
117 .iter()
118 .flat_map(|manifest| {
119 manifest
120 .metadata()
121 .repository
122 .iter()
123 .filter_map(|(name, elements)| {
124 if name.to_string().contains(&package_req.name().to_string()) {
125 Some((
126 name,
127 elements
128 .keys()
129 .filter(|version| {
130 package_req.version_req().matches(version)
131 })
132 .sorted_by(|a, b| Ord::cmp(b, a))
133 .collect_vec(),
134 ))
135 } else {
136 None
137 }
138 })
139 })
140 .collect(),
141 Impl::LocalPackageLocks(locks) => locks
142 .iter()
143 .flat_map(|lock| lock.rocks().values())
144 .filter_map(|package| {
145 let name = package.name();
148 if name.to_string().contains(&package_req.name().to_string()) {
149 Some((name, vec![package.version()]))
150 } else {
151 None
152 }
153 })
154 .collect_vec(),
155 }
156 }
157
158 pub(crate) fn latest_version(&self, rock_name: &PackageName) -> Option<PackageVersion> {
160 self.latest_match(&rock_name.clone().into(), None)
161 .map(|result| result.version().clone())
162 }
163
164 pub fn latest_match(
166 &self,
167 package_req: &PackageReq,
168 filter: Option<RemotePackageTypeFilterSpec>,
169 ) -> Option<PackageSpec> {
170 match self.find(package_req, filter) {
171 Ok(result) => Some(result.package),
172 Err(_) => None,
173 }
174 }
175}
176
177impl From<Manifest> for RemotePackageDB {
178 fn from(manifest: Manifest) -> Self {
179 Self(Impl::LuarocksManifests(vec![manifest]))
180 }
181}
182
183impl From<Vec<LocalPackageLock>> for RemotePackageDB {
184 fn from(locks: Vec<LocalPackageLock>) -> Self {
185 Self(Impl::LocalPackageLocks(locks))
186 }
187}