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::progress::{Progress, ProgressBar};
11use crate::{
12    config::Config,
13    package::{PackageReq, RemotePackage},
14    remote_package_source::RemotePackageSource,
15};
16
17pub mod metadata;
18mod metadata_from_server;
19mod metadata_from_vendor_dir;
20
21#[derive(Error, Debug, Diagnostic)]
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}
41
42impl Manifest {
43    pub fn new(server_url: Url, metadata: ManifestMetadata) -> Self {
44        Self {
45            server_url,
46            metadata,
47        }
48    }
49
50    pub async fn from_config(
51        server_url: Url,
52        config: &Config,
53        progress: &Progress<ProgressBar>,
54    ) -> Result<Self, ManifestError> {
55        if let Some(vendor_dir) = config.vendor_dir() {
56            let server_url: Url = Url::from_file_path(vendor_dir)
57                .map_err(|_err| ManifestError::Vendor(vendor_dir.to_slash_lossy().to_string()))?;
58            return Ok(Self::new(server_url, manifest_from_vendor_dir(vendor_dir)));
59        }
60        let content = manifest_from_cache_or_server(&server_url, config, progress)
61            .await
62            .map_err(|source| ManifestError::Server {
63                url: server_url.to_string(),
64                source: Box::new(source),
65            })?;
66        match ManifestMetadata::new(&content) {
67            Ok(metadata) => Ok(Self::new(server_url, metadata)),
68            Err(_) => {
69                let manifest = manifest_from_server_only(&server_url, config, progress)
70                    .await
71                    .map_err(|source| ManifestError::Server {
72                        url: server_url.to_string(),
73                        source: Box::new(source),
74                    })?;
75                Ok(Self::new(server_url, ManifestMetadata::new(&manifest)?))
76            }
77        }
78    }
79
80    pub fn server_url(&self) -> &Url {
81        &self.server_url
82    }
83
84    pub fn metadata(&self) -> &ManifestMetadata {
85        &self.metadata
86    }
87
88    /// Find a package that matches the requirement, returning the latest match
89    pub fn find(
90        &self,
91        package_req: &PackageReq,
92        filter: Option<RemotePackageTypeFilterSpec>,
93    ) -> Option<RemotePackage> {
94        match self.metadata().latest_match(package_req, filter) {
95            None => None,
96            Some((package, package_type)) => {
97                let remote_source = match package_type {
98                    RemotePackageType::Rockspec => {
99                        RemotePackageSource::LuarocksRockspec(self.server_url().clone())
100                    }
101                    RemotePackageType::Src => {
102                        RemotePackageSource::LuarocksSrcRock(self.server_url().clone())
103                    }
104                    RemotePackageType::Binary => {
105                        RemotePackageSource::LuarocksBinaryRock(self.server_url().clone())
106                    }
107                };
108                Some(RemotePackage::new(package, remote_source, None))
109            }
110        }
111    }
112}