Skip to main content

lux_lib/manifest/
mod.rs

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