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 server:\n{0}")]
27 #[diagnostic(forward(0))]
28 Server(#[from] ManifestFromServerError),
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}
38
39impl Manifest {
40 pub fn new(server_url: Url, metadata: ManifestMetadata) -> Self {
41 Self {
42 server_url,
43 metadata,
44 }
45 }
46
47 pub async fn from_config(
48 server_url: Url,
49 config: &Config,
50 progress: &Progress<ProgressBar>,
51 ) -> Result<Self, ManifestError> {
52 if let Some(vendor_dir) = config.vendor_dir() {
53 let server_url: Url = Url::from_file_path(vendor_dir)
54 .map_err(|_err| ManifestError::Vendor(vendor_dir.to_slash_lossy().to_string()))?;
55 return Ok(Self::new(server_url, manifest_from_vendor_dir(vendor_dir)));
56 }
57 let content = manifest_from_cache_or_server(&server_url, config, progress).await?;
58 match ManifestMetadata::new(&content) {
59 Ok(metadata) => Ok(Self::new(server_url, metadata)),
60 Err(_) => {
61 let manifest = manifest_from_server_only(&server_url, config, progress).await?;
62 Ok(Self::new(server_url, ManifestMetadata::new(&manifest)?))
63 }
64 }
65 }
66
67 pub fn server_url(&self) -> &Url {
68 &self.server_url
69 }
70
71 pub fn metadata(&self) -> &ManifestMetadata {
72 &self.metadata
73 }
74
75 pub fn find(
77 &self,
78 package_req: &PackageReq,
79 filter: Option<RemotePackageTypeFilterSpec>,
80 ) -> Option<RemotePackage> {
81 match self.metadata().latest_match(package_req, filter) {
82 None => None,
83 Some((package, package_type)) => {
84 let remote_source = match package_type {
85 RemotePackageType::Rockspec => {
86 RemotePackageSource::LuarocksRockspec(self.server_url().clone())
87 }
88 RemotePackageType::Src => {
89 RemotePackageSource::LuarocksSrcRock(self.server_url().clone())
90 }
91 RemotePackageType::Binary => {
92 RemotePackageSource::LuarocksBinaryRock(self.server_url().clone())
93 }
94 };
95 Some(RemotePackage::new(package, remote_source, None))
96 }
97 }
98 }
99}