warg_loader/
config.rs

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/// Configuration for [`super::Client`].
14#[derive(Clone, Default)]
15pub struct ClientConfig {
16    /// The default registry name.
17    default_registry: Option<String>,
18    /// Per-namespace registry, overriding `default_registry` (if present).
19    namespace_registries: HashMap<String, String>,
20    /// Per-registry configuration.
21    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/// Configuration for a specific registry.
122#[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}