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 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}