assemble_core/dependencies/
registry_container.rs

1use crate::cache::AssembleCache;
2use crate::dependencies::file_dependency::FileSystem;
3use crate::dependencies::DependencyType;
4use std::collections::hash_map::IntoValues;
5use std::collections::{HashMap, HashSet};
6use std::fmt::{Debug, Formatter};
7
8use std::path::PathBuf;
9use url::Url;
10
11/// A registry is some repository that interprets can interpret some [`Url`](url::Url)
12pub trait Registry {
13    /// Gets the base url this registry is located at.
14    fn url(&self) -> Url;
15
16    /// The dependency types that this registry supports
17    fn supported(&self) -> Vec<DependencyType>;
18}
19
20assert_obj_safe!(Registry);
21
22/// A container of registries
23pub struct RegistryContainer {
24    type_to_registry_index: HashMap<DependencyType, HashSet<usize>>,
25    registries: Vec<Box<dyn Registry>>,
26    cache_location: PathBuf,
27}
28
29impl Debug for RegistryContainer {
30    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31        f.debug_struct("RegistryContainer")
32            .field("len", &self.registries.len())
33            .finish()
34    }
35}
36
37unsafe impl Send for RegistryContainer {}
38unsafe impl Sync for RegistryContainer {}
39
40impl Default for RegistryContainer {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl RegistryContainer {
47    /// Creates a new registry container. Always contains the default `FileSystem`.
48    pub fn new() -> Self {
49        let mut container = Self {
50            type_to_registry_index: Default::default(),
51            registries: vec![],
52            cache_location: AssembleCache::default().to_path_buf(),
53        };
54        container.add_registry(FileSystem::default());
55        container
56    }
57
58    /// Add a registry to this container
59    pub fn add_registry<R: Registry + 'static>(&mut self, registry: R) {
60        let index = self.registries.len();
61        let boxed = Box::new(registry) as Box<dyn Registry>;
62        let dependency_types = boxed.supported();
63        self.registries.push(boxed);
64
65        for dependency_type in dependency_types {
66            self.type_to_registry_index
67                .entry(dependency_type)
68                .or_default()
69                .insert(index);
70        }
71    }
72
73    /// Gets the amount of registries in this container
74    pub fn len(&self) -> usize {
75        self.registries.len()
76    }
77
78    /// Gets the supported registries for a given dependency type
79    pub fn supported_registries(&self, ty: &DependencyType) -> RegistrySet {
80        if let Some(entry) = self.type_to_registry_index.get(ty) {
81            entry
82                .iter()
83                .copied()
84                .map(|index| &*self.registries[index])
85                .collect()
86        } else {
87            RegistrySet::default()
88        }
89    }
90
91    /// Gets the intersection of supported registries for given dependency types
92    pub fn supported_registries_intersection<'a, I>(&'a self, ty: I) -> RegistrySet<'a>
93    where
94        I: IntoIterator<Item = &'a DependencyType>,
95    {
96        ty.into_iter()
97            .map(|ty| self.supported_registries(ty))
98            .fold(RegistrySet::default(), RegistrySet::intersection)
99    }
100
101    /// Set the location where files should be downloaded to
102    pub fn set_cache_location(&mut self, cache_location: PathBuf) {
103        self.cache_location = cache_location;
104    }
105
106    /// Get where files should be downloaded to
107    pub fn cache_location(&self) -> &PathBuf {
108        &self.cache_location
109    }
110}
111
112#[derive(Default)]
113pub struct RegistrySet<'a> {
114    map: HashMap<Url, &'a dyn Registry>,
115}
116
117impl<'a> IntoIterator for RegistrySet<'a> {
118    type Item = &'a dyn Registry;
119    type IntoIter = IntoValues<Url, &'a dyn Registry>;
120
121    fn into_iter(self) -> Self::IntoIter {
122        self.map.into_values()
123    }
124}
125
126impl<'a> RegistrySet<'a> {
127    /// The intersection of two registry sets
128    pub fn intersection(self, other: Self) -> Self {
129        let mut map = HashMap::new();
130        for (url, reg) in self.map {
131            if other.map.contains_key(&url) {
132                map.insert(url, reg);
133            }
134        }
135        Self { map }
136    }
137
138    /// Gets the registries in this set
139    pub fn registries(&self) -> impl IntoIterator<Item = &'a dyn Registry> {
140        self.map.values().copied().collect::<Vec<_>>()
141    }
142}
143
144impl<'a> FromIterator<&'a dyn Registry> for RegistrySet<'a> {
145    fn from_iter<T: IntoIterator<Item = &'a dyn Registry>>(iter: T) -> Self {
146        Self {
147            map: iter
148                .into_iter()
149                .map(|reg: &dyn Registry| (reg.url(), reg))
150                .collect(),
151        }
152    }
153}