Skip to main content

lux_lib/manifest/
mod.rs

1use miette::Diagnostic;
2use path_slash::PathExt;
3use thiserror::Error;
4use url::Url;
5
6pub use crate::manifest::metadata::*;
7use crate::manifest::metadata_from_server::*;
8use crate::manifest::metadata_from_vendor_dir::manifest_from_vendor_dir;
9use crate::package::{RemotePackageType, RemotePackageTypeFilterSpec};
10use crate::{
11    config::Config,
12    package::{PackageReq, RemotePackage},
13    remote_package_source::RemotePackageSource,
14};
15
16pub mod metadata;
17mod metadata_from_server;
18mod metadata_from_vendor_dir;
19
20#[derive(Error, Debug, Diagnostic)]
21#[non_exhaustive]
22pub enum ManifestError {
23    #[error(transparent)]
24    #[diagnostic(transparent)]
25    Lua(#[from] ManifestLuaError),
26    #[error("failed to fetch manifest from {url}:\n{source}")]
27    #[diagnostic(forward(source))]
28    Server {
29        url: String,
30        source: Box<ManifestFromServerError>,
31    },
32    #[error("error parsing URL from `vendor-dir`: {0}:")]
33    Vendor(String),
34}
35
36#[derive(Clone, Debug)]
37pub(crate) struct Manifest {
38    server_url: Url,
39    metadata: ManifestMetadata,
40    default_filter: RemotePackageTypeFilterSpec,
41}
42
43impl Manifest {
44    pub fn new(
45        server_url: Url,
46        metadata: ManifestMetadata,
47        default_filter: RemotePackageTypeFilterSpec,
48    ) -> Self {
49        Self {
50            server_url,
51            metadata,
52            default_filter,
53        }
54    }
55
56    #[tracing::instrument(level = "trace", skip(config))]
57    pub async fn from_config(server_url: Url, config: &Config) -> Result<Self, ManifestError> {
58        if let Some(vendor_dir) = config.vendor_dir() {
59            let server_url: Url = Url::from_file_path(vendor_dir)
60                .map_err(|_err| ManifestError::Vendor(vendor_dir.to_slash_lossy().to_string()))?;
61            return Ok(Self::new(
62                server_url,
63                manifest_from_vendor_dir(vendor_dir),
64                config.package_types().clone(),
65            ));
66        }
67        let content = manifest_from_cache_or_server(&server_url, config)
68            .await
69            .map_err(|source| ManifestError::Server {
70                url: server_url.to_string(),
71                source: Box::new(source),
72            })?;
73        match ManifestMetadata::new(&content) {
74            Ok(metadata) => Ok(Self::new(
75                server_url,
76                metadata,
77                config.package_types().clone(),
78            )),
79            Err(_) => {
80                let manifest = manifest_from_server_only(&server_url, config)
81                    .await
82                    .map_err(|source| ManifestError::Server {
83                        url: server_url.to_string(),
84                        source: Box::new(source),
85                    })?;
86                Ok(Self::new(
87                    server_url,
88                    ManifestMetadata::new(&manifest)?,
89                    config.package_types().clone(),
90                ))
91            }
92        }
93    }
94
95    pub fn server_url(&self) -> &Url {
96        &self.server_url
97    }
98
99    pub fn metadata(&self) -> &ManifestMetadata {
100        &self.metadata
101    }
102
103    /// Find a package that matches the requirement, returning the latest match
104    pub fn find(
105        &self,
106        package_req: &PackageReq,
107        filter: Option<RemotePackageTypeFilterSpec>,
108    ) -> Option<RemotePackage> {
109        let filter = filter.as_ref().unwrap_or(&self.default_filter);
110        match self.metadata().latest_match(package_req, filter) {
111            None => None,
112            Some((package, package_type)) => {
113                let remote_source = match package_type {
114                    RemotePackageType::Rockspec => {
115                        RemotePackageSource::LuarocksRockspec(self.server_url().clone())
116                    }
117                    RemotePackageType::Src => {
118                        RemotePackageSource::LuarocksSrcRock(self.server_url().clone())
119                    }
120                    RemotePackageType::Binary => {
121                        RemotePackageSource::LuarocksBinaryRock(self.server_url().clone())
122                    }
123                };
124                Some(RemotePackage::new(package, remote_source, None))
125            }
126        }
127    }
128}