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