facto_core/registries/
manager.rs1use 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 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}