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)]
21pub enum ManifestError {
22 #[error(transparent)]
23 #[diagnostic(transparent)]
24 Lua(#[from] ManifestLuaError),
25 #[error("failed to fetch manifest from {url}:\n{source}")]
26 #[diagnostic(forward(source))]
27 Server {
28 url: String,
29 source: Box<ManifestFromServerError>,
30 },
31 #[error("error parsing URL from `vendor-dir`: {0}:")]
32 Vendor(String),
33}
34
35#[derive(Clone, Debug)]
36pub(crate) struct Manifest {
37 server_url: Url,
38 metadata: ManifestMetadata,
39}
40
41impl Manifest {
42 pub fn new(server_url: Url, metadata: ManifestMetadata) -> Self {
43 Self {
44 server_url,
45 metadata,
46 }
47 }
48
49 pub async fn from_config(server_url: Url, config: &Config) -> Result<Self, ManifestError> {
50 if let Some(vendor_dir) = config.vendor_dir() {
51 let server_url: Url = Url::from_file_path(vendor_dir)
52 .map_err(|_err| ManifestError::Vendor(vendor_dir.to_slash_lossy().to_string()))?;
53 return Ok(Self::new(server_url, manifest_from_vendor_dir(vendor_dir)));
54 }
55 let content = manifest_from_cache_or_server(&server_url, config)
56 .await
57 .map_err(|source| ManifestError::Server {
58 url: server_url.to_string(),
59 source: Box::new(source),
60 })?;
61 match ManifestMetadata::new(&content) {
62 Ok(metadata) => Ok(Self::new(server_url, metadata)),
63 Err(_) => {
64 let manifest = manifest_from_server_only(&server_url, config)
65 .await
66 .map_err(|source| ManifestError::Server {
67 url: server_url.to_string(),
68 source: Box::new(source),
69 })?;
70 Ok(Self::new(server_url, ManifestMetadata::new(&manifest)?))
71 }
72 }
73 }
74
75 pub fn server_url(&self) -> &Url {
76 &self.server_url
77 }
78
79 pub fn metadata(&self) -> &ManifestMetadata {
80 &self.metadata
81 }
82
83 pub fn find(
85 &self,
86 package_req: &PackageReq,
87 filter: Option<RemotePackageTypeFilterSpec>,
88 ) -> Option<RemotePackage> {
89 match self.metadata().latest_match(package_req, filter) {
90 None => None,
91 Some((package, package_type)) => {
92 let remote_source = match package_type {
93 RemotePackageType::Rockspec => {
94 RemotePackageSource::LuarocksRockspec(self.server_url().clone())
95 }
96 RemotePackageType::Src => {
97 RemotePackageSource::LuarocksSrcRock(self.server_url().clone())
98 }
99 RemotePackageType::Binary => {
100 RemotePackageSource::LuarocksBinaryRock(self.server_url().clone())
101 }
102 };
103 Some(RemotePackage::new(package, remote_source, None))
104 }
105 }
106 }
107}