Skip to main content

facto_core/registries/
manager.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use futures::stream::{self, StreamExt};
5
6use crate::config::Config;
7use crate::http;
8use crate::registries::cache::CachedRegistry;
9use crate::registries::*;
10
11pub struct RegistryManager {
12    registries: HashMap<String, Arc<dyn Registry>>,
13}
14
15impl RegistryManager {
16    pub fn new(config: &Config) -> Result<Self, http::HttpClientError> {
17        let mut registries: HashMap<String, Arc<dyn Registry>> = HashMap::new();
18        let client = http::default_client()?;
19        let ttl = config.cache.clone();
20
21        for id in &config.registries.enabled {
22            let registry: Option<Arc<dyn Registry>> = match id.as_str() {
23                "pypi" => Some(Arc::new(CachedRegistry::new(
24                    crate::registries::pypi::PyPI::new(client.clone()),
25                    ttl.clone(),
26                ))),
27                "npm" => Some(Arc::new(CachedRegistry::new(
28                    crate::registries::npm::Npm::new(client.clone()),
29                    ttl.clone(),
30                ))),
31                "crates" => Some(Arc::new(CachedRegistry::new(
32                    crate::registries::crates_io::CratesIo::new(client.clone()),
33                    ttl.clone(),
34                ))),
35                "go" => Some(Arc::new(CachedRegistry::new(
36                    crate::registries::go::GoModules::new(client.clone()),
37                    ttl.clone(),
38                ))),
39                "rubygems" => Some(Arc::new(CachedRegistry::new(
40                    crate::registries::rubygems::RubyGems::new(client.clone()),
41                    ttl.clone(),
42                ))),
43                "maven" => Some(Arc::new(CachedRegistry::new(
44                    crate::registries::maven::Maven::new(client.clone()),
45                    ttl.clone(),
46                ))),
47                "nuget" => Some(Arc::new(CachedRegistry::new(
48                    crate::registries::nuget::NuGet::new(client.clone()),
49                    ttl.clone(),
50                ))),
51                "packagist" => Some(Arc::new(CachedRegistry::new(
52                    crate::registries::packagist::Packagist::new(client.clone()),
53                    ttl.clone(),
54                ))),
55                "dockerhub" => Some(Arc::new(CachedRegistry::new(
56                    crate::registries::dockerhub::DockerHub::new(client.clone()),
57                    ttl.clone(),
58                ))),
59                _ => {
60                    tracing::warn!("unknown registry: {}", id);
61                    None
62                }
63            };
64
65            if let Some(r) = registry {
66                registries.insert(id.clone(), r);
67            }
68        }
69
70        Ok(Self { registries })
71    }
72
73    pub fn list_registries(&self) -> Vec<(String, String)> {
74        self.registries
75            .iter()
76            .map(|(id, r)| (id.clone(), r.display_name().to_string()))
77            .collect()
78    }
79
80    pub fn get_registry(&self, id: &str) -> Option<&Arc<dyn Registry>> {
81        self.registries.get(id)
82    }
83
84    /// For each dependency, look up the latest stable version on its
85    /// registry and flag whether it is outdated. Concurrency-bounded.
86    pub async fn check_deps(
87        &self,
88        deps: Vec<crate::lockfiles::ResolvedDep>,
89    ) -> Vec<crate::lockfiles::CheckedDep> {
90        stream::iter(deps)
91            .map(|dep| {
92                let registry = self.registries.get(&dep.registry).cloned();
93                async move {
94                    let Some(registry) = registry else {
95                        return crate::lockfiles::CheckedDep {
96                            name: dep.name,
97                            current: dep.version,
98                            latest: None,
99                            outdated: None,
100                            registry: dep.registry,
101                            group: dep.group,
102                            error: Some("unknown registry".to_string()),
103                        };
104                    };
105                    match registry.get_versions(&dep.name).await {
106                        Ok(versions) => {
107                            let latest = versions
108                                .into_iter()
109                                .find(|v| !v.prerelease)
110                                .map(|v| v.version);
111                            let outdated = latest.as_ref().map(|l| l != &dep.version);
112                            crate::lockfiles::CheckedDep {
113                                name: dep.name,
114                                current: dep.version,
115                                latest,
116                                outdated,
117                                registry: dep.registry,
118                                group: dep.group,
119                                error: None,
120                            }
121                        }
122                        Err(e) => crate::lockfiles::CheckedDep {
123                            name: dep.name,
124                            current: dep.version,
125                            latest: None,
126                            outdated: None,
127                            registry: dep.registry,
128                            group: dep.group,
129                            error: Some(e.to_string()),
130                        },
131                    }
132                }
133            })
134            .buffer_unordered(16)
135            .collect()
136            .await
137    }
138}