assemble_core/flow/
output.rs

1//! The outputs of the assemble project
2
3use crate::dependencies::file_dependency::FILE_SYSTEM_TYPE;
4use crate::dependencies::{
5    AcquisitionError, Dependency, DependencyType, Registry, ResolvedDependency,
6    ResolvedDependencyBuilder,
7};
8use crate::file_collection::FileSet;
9use crate::flow::shared::{Artifact, ConfigurableArtifact, IntoArtifact};
10use crate::identifier::Id;
11
12use crate::lazy_evaluation::{Prop, Provider};
13use crate::project::buildable::{BuildableObject, GetBuildable, IntoBuildable};
14use crate::task::{BuildableTask, HasTaskId, TaskHandle};
15use crate::{Executable, Task};
16use once_cell::sync::Lazy;
17use std::collections::HashMap;
18
19use std::path::{Path, PathBuf};
20
21/// The outgoing variant handler
22#[derive(Default, Debug)]
23pub struct VariantHandler {
24    default_variant: Option<String>,
25    variant_map: HashMap<String, Prop<ConfigurableArtifact>>,
26}
27
28impl VariantHandler {
29    pub fn new() -> Self {
30        Self {
31            default_variant: None,
32            variant_map: Default::default(),
33        }
34    }
35
36    /// Set the default outgoing variant
37    pub fn set_default(&mut self, default: &str) {
38        self.default_variant = Some(default.to_string());
39    }
40
41    /// Get the default variant name
42    pub fn default(&self) -> String {
43        self.default_variant
44            .as_ref()
45            .cloned()
46            .or_else(|| {
47                if self.variant_map.len() == 1 {
48                    self.variant_map.keys().next().cloned()
49                } else {
50                    None
51                }
52            })
53            .expect("no default variant could be determined")
54    }
55
56    /// Adds an artifact for a configuration
57    pub fn add<S, A>(&mut self, variant: S, artifact: A)
58    where
59        S: AsRef<str>,
60        A: IntoArtifact + Send + 'static,
61        A::IntoArtifact: 'static,
62    {
63        let config_name = variant.as_ref().to_string();
64        let mut prop = Prop::<ConfigurableArtifact>::new(Id::from(&*config_name));
65        prop.set_with(Lazy::new(move || {
66            let artifact = artifact.into_artifact();
67            let buildable = artifact.buildable();
68            let mut output = ConfigurableArtifact::from_artifact(artifact);
69            if let Some(buildable) = buildable {
70                output.built_by(buildable);
71            }
72            output
73        }))
74        .unwrap();
75
76        self.variant_map.insert(config_name, prop);
77    }
78
79    pub fn add_with<S, A, F>(&mut self, configuration: S, artifact: A, config: F)
80    where
81        S: AsRef<str>,
82        A: IntoArtifact + Send,
83        A::IntoArtifact: 'static,
84        F: FnOnce(&mut ConfigurableArtifact) + Send,
85    {
86        let config_name = configuration.as_ref().to_string();
87        let mut prop = Prop::<ConfigurableArtifact>::new(Id::from(&*config_name));
88        let artifact = artifact.into_artifact();
89        let mut configurable = ConfigurableArtifact::from_artifact(artifact);
90        (config)(&mut configurable);
91        prop.set(configurable).unwrap();
92        self.variant_map.insert(config_name, prop);
93    }
94
95    pub(crate) fn get_artifact(
96        &self,
97        configuration: &str,
98    ) -> Option<impl Provider<ConfigurableArtifact>> {
99        self.variant_map.get(configuration).cloned()
100    }
101}
102
103pub trait SinglePathOutputTask: Task + Send + Sync + 'static {
104    fn get_path(task: &Executable<Self>) -> PathBuf;
105}
106
107impl<T: SinglePathOutputTask> ArtifactTask for T {
108    fn get_artifact(task: &Executable<Self>) -> ConfigurableArtifact {
109        let mut output = ConfigurableArtifact::from_artifact(T::get_path(task));
110        output.built_by(task.task_id());
111        output
112    }
113}
114
115impl<T: SinglePathOutputTask> Provider<PathBuf> for TaskHandle<T> {
116    fn try_get(&self) -> Option<PathBuf> {
117        self.provides(|e| T::get_path(e)).try_get()
118    }
119}
120
121/// A task that produces an artifact
122pub trait ArtifactTask: Task + Send + Sync + 'static {
123    /// Get the artifact produced by this task.
124    fn get_artifact(task: &Executable<Self>) -> ConfigurableArtifact;
125}
126
127impl<A: ArtifactTask> IntoArtifact for TaskHandle<A> {
128    type IntoArtifact = ConfigurableArtifact;
129
130    fn into_artifact(self) -> Self::IntoArtifact {
131        (&self).into_artifact()
132    }
133}
134
135impl<A: ArtifactTask> IntoArtifact for &TaskHandle<A> {
136    type IntoArtifact = ConfigurableArtifact;
137
138    fn into_artifact(self) -> Self::IntoArtifact {
139        self.provides(|s| A::get_artifact(s)).get()
140    }
141}
142
143impl<AT: ArtifactTask> GetBuildable for Executable<AT> {
144    fn as_buildable(&self) -> BuildableObject {
145        BuildableObject::new(self.clone().into_buildable())
146    }
147}
148
149impl<AT: ArtifactTask> Dependency for Executable<AT> {
150    fn id(&self) -> String {
151        AT::get_artifact(self).file().to_str().unwrap().to_string()
152    }
153
154    fn dep_type(&self) -> DependencyType {
155        FILE_SYSTEM_TYPE.clone()
156    }
157
158    fn try_resolve(
159        &self,
160        _: &dyn Registry,
161        _: &Path,
162    ) -> Result<ResolvedDependency, AcquisitionError> {
163        Ok(ResolvedDependencyBuilder::new(AT::get_artifact(self))
164            .built_by(self.built_by())
165            .finish())
166    }
167
168    // fn maybe_buildable(&self) -> Option<Box<dyn Buildable>> {
169    //     Some(Box::new(self.task_id().clone()))
170    // }
171}
172
173impl<AT: ArtifactTask + Send + 'static> Dependency for TaskHandle<AT> {
174    fn id(&self) -> String {
175        self.provides(|s| s.id()).get()
176    }
177
178    fn dep_type(&self) -> DependencyType {
179        FILE_SYSTEM_TYPE.clone()
180    }
181
182    fn try_resolve(
183        &self,
184        _: &dyn Registry,
185        _: &Path,
186    ) -> Result<ResolvedDependency, AcquisitionError> {
187        Ok(
188            ResolvedDependencyBuilder::new(self.provides(|s| AT::get_artifact(s)).get())
189                .built_by(self.clone())
190                .finish(),
191        )
192    }
193
194    // fn maybe_buildable(&self) -> Option<Box<dyn Buildable>> {
195    //     Some(Box::new(self.task_id().clone()))
196    // }
197}
198
199impl<T: ArtifactTask> From<TaskHandle<T>> for FileSet {
200    fn from(t: TaskHandle<T>) -> Self {
201        let artifact = t.provides(|e| T::get_artifact(e)).get();
202        let mut set = FileSet::from(artifact.file());
203        set.built_by(t);
204        set
205    }
206}
207//
208// /// Represents a variant that can be used as an output of a task
209// pub struct Variant<A: ArtifactTask> {
210//     outgoing_artifact: A,
211//     built_by: BuiltByContainer,
212// }
213//
214// impl<A: Artifact> Variant<A> {
215//     /// Create a new variant using an outgoing variant
216//     fn new<I>(outgoing_artifact: I) -> Self
217//     where
218//         I: IntoArtifact<IntoArtifact = A>,
219//     {
220//         let artifact = outgoing_artifact.into_artifact();
221//         let built_by = artifact
222//             .buildable()
223//             .map(|b| BuiltByContainer::with_buildable(b))
224//             .unwrap_or(BuiltByContainer::new());
225//         Self {
226//             outgoing_artifact: artifact,
227//             built_by,
228//         }
229//     }
230//
231//     /// Add something that this variant is built by
232//     pub fn built_by<T: IntoBuildable>(&mut self, buildable: T)
233//     where
234//         <T as IntoBuildable>::Buildable: 'static,
235//     {
236//         self.built_by.add(buildable)
237//     }
238// }