1mod release;
2pub mod source;
3
4use std::collections::HashMap;
5
6use anyhow::anyhow;
7use bytes::Bytes;
8use futures_util::stream::BoxStream;
9
10use wasm_pkg_common::metadata::RegistryMetadata;
11
12use crate::source::{
13 local::LocalSource, oci::OciSource, warg::WargSource, PackageSource, VersionInfo,
14};
15
16pub use oci_distribution::client as oci_client;
18
19pub use wasm_pkg_common::{
20 config::Config,
21 package::{PackageRef, Version},
22 registry::Registry,
23 Error,
24};
25
26pub use crate::release::{ContentDigest, Release};
27
28pub struct Client {
30 config: Config,
31 sources: HashMap<Registry, Box<dyn PackageSource>>,
32}
33
34impl Client {
35 pub fn new(config: Config) -> Self {
37 Self {
38 config,
39 sources: Default::default(),
40 }
41 }
42
43 pub fn with_global_defaults() -> Result<Self, Error> {
45 let config = Config::global_defaults()?;
46 Ok(Self::new(config))
47 }
48
49 pub async fn list_all_versions(
51 &mut self,
52 package: &PackageRef,
53 ) -> Result<Vec<VersionInfo>, Error> {
54 let source = self.resolve_source(package).await?;
55 source.list_all_versions(package).await
56 }
57
58 pub async fn get_release(
60 &mut self,
61 package: &PackageRef,
62 version: &Version,
63 ) -> Result<Release, Error> {
64 let source = self.resolve_source(package).await?;
65 source.get_release(package, version).await
66 }
67
68 pub async fn stream_content(
71 &mut self,
72 package: &PackageRef,
73 release: &Release,
74 ) -> Result<BoxStream<Result<Bytes, Error>>, Error> {
75 let source = self.resolve_source(package).await?;
76 source.stream_content(package, release).await
77 }
78
79 async fn resolve_source(
80 &mut self,
81 package: &PackageRef,
82 ) -> Result<&mut dyn PackageSource, Error> {
83 let registry = self
84 .config
85 .resolve_registry(package)
86 .ok_or_else(|| Error::NoRegistryForNamespace(package.namespace().clone()))?
87 .to_owned();
88 if !self.sources.contains_key(®istry) {
89 let registry_config = self
90 .config
91 .registry_config(®istry)
92 .cloned()
93 .unwrap_or_default();
94
95 let should_fetch_meta = registry_config.backend_type() != Some("local");
97 let registry_meta = if should_fetch_meta {
98 RegistryMetadata::fetch_or_default(®istry).await
99 } else {
100 RegistryMetadata::default()
101 };
102
103 let backend_type = match registry_config.backend_type() {
105 Some(backend_type) => Some(backend_type),
107 None => {
108 let preferred_protocol = registry_meta.preferred_protocol();
110 if preferred_protocol == Some("local") {
112 return Err(Error::InvalidRegistryMetadata(anyhow!(
113 "registry metadata with 'local' protocol not allowed"
114 )));
115 }
116 preferred_protocol
117 }
118 }
119 .unwrap_or("oci");
121 tracing::debug!(?backend_type, "Resolved backend type");
122
123 let source: Box<dyn PackageSource> = match backend_type {
124 "local" => Box::new(LocalSource::new(registry_config)?),
125 "oci" => Box::new(OciSource::new(®istry, ®istry_config, ®istry_meta)?),
126 "warg" => {
127 Box::new(WargSource::new(®istry, ®istry_config, ®istry_meta).await?)
128 }
129 other => {
130 return Err(Error::InvalidConfig(anyhow!(
131 "unknown backend type {other:?}"
132 )));
133 }
134 };
135 self.sources.insert(registry.clone(), source);
136 }
137 Ok(self.sources.get_mut(®istry).unwrap().as_mut())
138 }
139}