assemble_core/dependencies/
configurations.rs

1//! A configuration has two states: resolved and unresolved
2
3use crate::__export::TaskId;
4use crate::dependencies::{
5    AcquisitionError, Dependency, IntoDependency, RegistryContainer, ResolvedDependency,
6};
7use crate::file_collection::{FileCollection, FileSet};
8use crate::flow::shared::{Artifact, ImmutableArtifact};
9
10use crate::lazy_evaluation::Provider;
11use crate::prelude::ProjectResult;
12use crate::project::buildable::{Buildable, BuildableObject, BuiltByContainer, GetBuildable};
13
14use crate::Project;
15use once_cell::sync::OnceCell;
16use std::collections::HashSet;
17use std::fmt::{Debug, Display, Formatter};
18use std::path::PathBuf;
19use std::sync::{Arc, Mutex};
20use crate::error::PayloadError;
21
22#[derive(Debug, Clone)]
23pub struct Configuration {
24    inner: Arc<Mutex<ConfigurationInner>>,
25}
26
27impl Configuration {
28    /// Create a new configuration
29    pub(crate) fn new(name: &str, registry_container: &Arc<Mutex<RegistryContainer>>) -> Self {
30        Self {
31            inner: Arc::new(Mutex::new(ConfigurationInner {
32                name: name.to_string(),
33                parents: vec![],
34                dependencies: vec![],
35                resolved: OnceCell::new(),
36                built_by: OnceCell::new(),
37                registry_container: registry_container.clone(),
38            })),
39        }
40    }
41
42    fn inner<R, F: FnOnce(&mut ConfigurationInner) -> R>(&self, func: F) -> R {
43        let mut inner = self.inner.lock().unwrap();
44        (func)(&mut inner)
45    }
46
47    fn inner_mut<R, F: FnOnce(&mut ConfigurationInner) -> R>(&mut self, func: F) -> R {
48        let mut inner = self.inner.lock().unwrap();
49        if inner.resolved.get().is_some() {
50            panic!("{} already resolved", inner);
51        }
52        (func)(&mut inner)
53    }
54
55    /// Gets the resolved form of this configuration.
56    ///
57    /// If the configuration hasn't been resolved yet, resolves it at this point.
58    pub fn resolved(&self) -> Result<ResolvedConfiguration, AcquisitionError> {
59        self.inner(ConfigurationInner::resolve)
60    }
61
62    /// Add a dependency to this configuration
63    pub fn add_dependency<D: IntoDependency>(&mut self, dependency: D)
64    where
65        D::IntoDep: 'static + Send + Sync,
66    {
67        let dependency = dependency.into_dependency();
68        self.inner_mut(move |config| config.dependencies.push(Box::new(dependency)))
69    }
70
71    /// Adds a configuration that this configuration extends from
72    pub fn extends_from(&mut self, other: &Configuration) {
73        self.inner_mut(|inner| {
74            inner.parents.push(other.clone());
75        })
76    }
77}
78
79impl Display for Configuration {
80    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
81        write!(f, "Configuration {:?}", self.inner.lock().unwrap().name)
82    }
83}
84
85impl Provider<FileSet> for Configuration {
86    fn try_get(&self) -> Option<FileSet> {
87        self.inner
88            .lock()
89            .unwrap()
90            .resolved
91            .get()
92            .map(|resolved| FileSet::from_iter(resolved.files()))
93    }
94}
95
96impl Buildable for Configuration {
97    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
98        self.inner.lock().map_err(PayloadError::new)?.get_dependencies(project)
99    }
100}
101
102struct ConfigurationInner {
103    name: String,
104    parents: Vec<Configuration>,
105    dependencies: Vec<Box<dyn Dependency + Send + Sync>>,
106    resolved: OnceCell<ResolvedConfiguration>,
107    built_by: OnceCell<BuildableObject>,
108
109    registry_container: Arc<Mutex<RegistryContainer>>,
110}
111
112impl ConfigurationInner {
113    fn resolve(&mut self) -> Result<ResolvedConfiguration, AcquisitionError> {
114        self.resolved
115            .get_or_try_init(|| {
116                let mut resolved = vec![];
117                let dependencies = self.dependencies.drain(..).collect::<Vec<_>>();
118
119                let mut built_by = BuiltByContainer::new();
120
121                'outer: for dependency in dependencies {
122                    debug!("attempting to resolve {}", dependency);
123
124                    built_by.add(dependency.as_buildable());
125
126                    let registry_c = self.registry_container.lock().unwrap();
127                    let mut errors = vec![];
128                    let mut found = false;
129                    for registry in registry_c.supported_registries(&dependency.dep_type()) {
130                        match dependency.try_resolve(registry, registry_c.cache_location()) {
131                            Ok(resolved_dep) => {
132                                resolved.push(resolved_dep);
133
134                                found = true;
135                                continue 'outer;
136                            }
137                            Err(e) => {
138                                errors.push(e);
139                            }
140                        }
141                    }
142
143                    if !found {
144                        return Err(AcquisitionError::from_iter(errors));
145                    }
146                }
147
148                self.built_by
149                    .set(BuildableObject::from(built_by))
150                    .expect("Shouldn't be set");
151
152                Ok(ResolvedConfiguration {
153                    dependencies: resolved,
154                })
155            })
156            .map(|res| res.clone())
157    }
158}
159
160impl Buildable for ConfigurationInner {
161    /// The dependencies to resolve this configuration
162    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
163        self.built_by
164            .get()
165            .map(|b| b.get_dependencies(project))
166            .unwrap_or_else(|| {
167                let mut output = HashSet::new();
168                for dep in &self.dependencies {
169                    trace!("Getting dependencies for dependency: {:#?}", dep);
170                    let buildable = dep.as_buildable();
171                    output.extend(buildable.get_dependencies(project)?);
172                }
173                Ok(output)
174            })
175    }
176}
177
178impl Debug for ConfigurationInner {
179    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
180        f.debug_struct(&format!("{}", self))
181            .field("parents", &self.parents)
182            .field("dependencies", &self.dependencies.len())
183            .field("is resolved", &self.resolved.get().is_some())
184            .finish()
185    }
186}
187
188impl Display for ConfigurationInner {
189    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
190        write!(f, "Configuration {:?}", self.name)
191    }
192}
193
194/// A configuration can only be resolved after all buildable dependencies have been completed
195#[derive(Debug, Clone)]
196pub struct ResolvedConfiguration {
197    dependencies: Vec<ResolvedDependency>,
198}
199
200impl Display for ResolvedConfiguration {
201    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
202        let mut list = f.debug_list();
203        for dep in &self.dependencies {
204            let artifacts = dep
205                .artifacts()
206                .into_iter()
207                .map(|s| ImmutableArtifact::new(s).file())
208                .collect::<Vec<_>>();
209            list.entries(artifacts);
210        }
211        list.finish()
212    }
213}
214
215impl FileCollection for ResolvedConfiguration {
216    fn files(&self) -> HashSet<PathBuf> {
217        self.dependencies
218            .iter()
219            .flat_map(|dep| {
220                let artifact_files = dep.artifact_files();
221                artifact_files.files()
222            })
223            .collect()
224    }
225}