1mod toml;
2
3use std::{collections::HashMap, path::PathBuf};
4
5use oci_distribution::client::ClientConfig as OciClientConfig;
6use secrecy::SecretString;
7
8use crate::{
9 source::{local::LocalConfig, oci::OciConfig, warg::WargConfig},
10 Error, PackageRef,
11};
12
13#[derive(Clone, Default)]
15pub struct ClientConfig {
16 default_registry: Option<String>,
18 namespace_registries: HashMap<String, String>,
20 pub(crate) registry_configs: HashMap<String, RegistryConfig>,
22}
23
24impl ClientConfig {
25 pub fn to_client(&self) -> crate::Client {
26 crate::Client::new(self.clone())
27 }
28
29 pub fn merge_config(&mut self, other: ClientConfig) -> &mut Self {
30 if let Some(default_registry) = other.default_registry {
31 self.default_registry(default_registry);
32 }
33 for (namespace, registry) in other.namespace_registries {
34 self.namespace_registry(namespace, registry);
35 }
36 for (registry, config) in other.registry_configs {
37 self.registry_configs.insert(registry, config);
38 }
39 self
40 }
41
42 pub fn default_registry(&mut self, registry: impl Into<String>) -> &mut Self {
43 self.default_registry = Some(registry.into());
44 self
45 }
46
47 pub fn namespace_registry(
48 &mut self,
49 namespace: impl Into<String>,
50 registry: impl Into<String>,
51 ) -> &mut Self {
52 self.namespace_registries
53 .insert(namespace.into(), registry.into());
54 self
55 }
56
57 pub fn local_registry_config(
58 &mut self,
59 registry: impl Into<String>,
60 root: impl Into<PathBuf>,
61 ) -> &mut Self {
62 self.registry_configs.insert(
63 registry.into(),
64 RegistryConfig::Local(LocalConfig { root: root.into() }),
65 );
66 self
67 }
68
69 pub fn oci_registry_config(
70 &mut self,
71 registry: impl Into<String>,
72 client_config: Option<OciClientConfig>,
73 credentials: Option<BasicCredentials>,
74 ) -> Result<&mut Self, Error> {
75 if client_config
76 .as_ref()
77 .is_some_and(|cfg| cfg.platform_resolver.is_some())
78 {
79 Error::InvalidConfig(anyhow::anyhow!(
80 "oci_distribution::client::ClientConfig::platform_resolver not supported"
81 ));
82 }
83 let cfg = RegistryConfig::Oci(OciConfig {
84 client_config,
85 credentials,
86 });
87 self.registry_configs.insert(registry.into(), cfg);
88 Ok(self)
89 }
90
91 pub fn warg_registry_config(
92 &mut self,
93 registry: impl Into<String>,
94 client_config: Option<warg_client::Config>,
95 auth_token: Option<impl Into<SecretString>>,
96 ) -> Result<&mut Self, Error> {
97 let cfg = RegistryConfig::Warg(WargConfig {
98 client_config: client_config.unwrap_or_default(),
99 auth_token: auth_token.map(Into::into),
100 });
101 self.registry_configs.insert(registry.into(), cfg);
102 Ok(self)
103 }
104
105 pub(crate) fn resolve_package_registry(&self, package: &PackageRef) -> Result<&str, Error> {
106 let namespace = package.namespace();
107 tracing::debug!("Resolving registry for {namespace:?}");
108
109 if let Some(registry) = self.namespace_registries.get(namespace.as_ref()) {
110 tracing::debug!("Found namespace-specific registry {registry:?}");
111 return Ok(registry);
112 }
113 if let Some(registry) = &self.default_registry {
114 tracing::debug!("No namespace-specific registry; using default {registry:?}");
115 return Ok(registry);
116 }
117 Err(Error::NoRegistryForNamespace(namespace.to_owned()))
118 }
119}
120
121#[derive(Clone, Debug)]
123pub enum RegistryConfig {
124 Local(LocalConfig),
125 Oci(OciConfig),
126 Warg(WargConfig),
127}
128
129impl Default for RegistryConfig {
130 fn default() -> Self {
131 Self::Oci(Default::default())
132 }
133}
134
135#[derive(Clone, Debug)]
136pub struct BasicCredentials {
137 pub username: String,
138 pub password: SecretString,
139}