assemble_core/project/
buildable.rs

1//! Define the buildable trait
2//!
3//! Buildable traits allows for easier constructing of project structures.
4//!
5//! # Buildable Types
6//! - `String`, `&str`, or [`TaskId`](crate::__export::TaskId)
7//! - Any type that implements [`TaskDependency`](TaskDependency)
8//! - Any type that implements [`Buildable`](Buildable)
9//! - [`FileCollection`](crate::file_collection::FileCollection)
10
11use crate::identifier::TaskId;
12
13use crate::project::ProjectResult;
14
15use crate::project::Project;
16
17use std::collections::HashSet;
18use std::fmt::{Debug, Formatter};
19use std::ops::{Deref, DerefMut};
20use std::sync::Arc;
21
22/// Represents something can be _built_ by the assemble project.
23pub trait IntoBuildable {
24    type Buildable: Buildable;
25    /// Returns a dependency which contains the tasks which build this object.
26    fn into_buildable(self) -> Self::Buildable;
27}
28
29pub trait GetBuildable {
30    /// Returns a dependency which contains the tasks which build this object.
31    fn as_buildable(&self) -> BuildableObject;
32}
33
34assert_obj_safe!(GetBuildable);
35
36impl<B: IntoBuildable + Clone> GetBuildable for B
37where
38    <B as IntoBuildable>::Buildable: 'static,
39{
40    fn as_buildable(&self) -> BuildableObject {
41        BuildableObject::new(self.clone().into_buildable())
42    }
43}
44
45impl<B: Buildable> IntoBuildable for B {
46    type Buildable = B;
47
48    fn into_buildable(self) -> B {
49        self
50    }
51}
52
53/// The tasks that are required to be built by this project to make this object. If this is a task,
54/// the task is also included.
55pub trait Buildable: Send + Sync + Debug {
56    /// Gets the dependencies required to build this task
57    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>>;
58}
59
60assert_obj_safe!(Buildable);
61
62impl<B: Buildable> Buildable for &B {
63    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
64        (*self).get_dependencies(project)
65    }
66}
67
68impl Buildable for Box<dyn Buildable + '_> {
69    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
70        self.as_ref().get_dependencies(project)
71    }
72}
73
74impl Buildable for Arc<dyn Buildable + '_> {
75    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
76        self.as_ref().get_dependencies(project)
77    }
78}
79
80impl<B: Buildable> Buildable for Vec<B> {
81    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
82        self.iter()
83            .map(|b| b.get_dependencies(project))
84            .collect::<Result<Vec<HashSet<_>>, _>>()
85            .map(|v| v.into_iter().flatten().collect())
86    }
87}
88
89/// A set of task dependencies
90#[derive(Default, Clone)]
91pub struct BuiltByContainer(Vec<Arc<dyn Buildable>>);
92
93impl BuiltByContainer {
94    /// Creates a new, empty built by container
95    pub const fn new() -> Self {
96        Self(Vec::new())
97    }
98
99    /// Creates a new, empty built by container with a preset capacity
100    pub fn with_capacity(capacity: usize) -> Self {
101        Self(Vec::with_capacity(capacity))
102    }
103
104    /// Creates a built by container that already contains a given buildable
105    pub fn with_buildable<B: IntoBuildable>(buildable: B) -> Self
106    where
107        <B as IntoBuildable>::Buildable: 'static,
108    {
109        let mut output = BuiltByContainer::with_capacity(1);
110        output.add(buildable);
111        output
112    }
113
114    /// Join two BuiltByContainers together
115    pub fn join(self, other: Self) -> Self {
116        let mut inner = self.0;
117        inner.extend(other.0);
118        Self(inner)
119    }
120
121    pub fn add<T: IntoBuildable>(&mut self, buildable: T)
122    where
123        <T as IntoBuildable>::Buildable: 'static,
124    {
125        let buildable: Arc<dyn Buildable> = Arc::new(buildable.into_buildable());
126        self.0.push(buildable);
127    }
128}
129
130impl Debug for BuiltByContainer {
131    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
132        write!(f, "BuiltByContainer ")?;
133        f.debug_set().entries(&self.0).finish()
134    }
135}
136
137impl Buildable for BuiltByContainer {
138    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
139        let mut output = HashSet::new();
140        for dep in &self.0 {
141            trace!("Getting dependencies for buildable: {:#?}", dep);
142            output.extend(dep.get_dependencies(project)?);
143        }
144        Ok(output)
145    }
146}
147
148/// Allows for adding "built by" info to non buildable objects
149#[derive(Debug)]
150pub struct BuiltBy<T: Debug> {
151    built_by: Arc<dyn Buildable>,
152    value: T,
153}
154
155impl<T: Debug + Clone> Clone for BuiltBy<T> {
156    fn clone(&self) -> Self {
157        Self {
158            built_by: self.built_by.clone(),
159            value: self.value.clone(),
160        }
161    }
162}
163
164impl<T: Debug> DerefMut for BuiltBy<T> {
165    fn deref_mut(&mut self) -> &mut Self::Target {
166        &mut self.value
167    }
168}
169
170impl<T: Debug> Deref for BuiltBy<T> {
171    type Target = T;
172
173    fn deref(&self) -> &Self::Target {
174        &self.value
175    }
176}
177
178impl<T: Debug> IntoBuildable for BuiltBy<T> {
179    type Buildable = Arc<dyn Buildable>;
180
181    fn into_buildable(self) -> Self::Buildable {
182        self.built_by
183    }
184}
185
186impl<T: Debug> BuiltBy<T> {
187    /// Create a new buildable object
188    pub fn new<B: IntoBuildable + 'static>(built_by: B, value: T) -> Self {
189        Self {
190            built_by: Arc::new(built_by.into_buildable()),
191            value,
192        }
193    }
194
195    /// Makes this into the inner value
196    pub fn into_inner(self) -> T {
197        self.value
198    }
199
200    /// Turns this built by into a built of a reference
201    pub fn as_ref(&self) -> BuiltBy<&T> {
202        BuiltBy {
203            built_by: self.built_by.clone(),
204            value: &self.value,
205        }
206    }
207
208    /// Gets a copy of the buildable
209    pub fn built_by(&self) -> &dyn Buildable {
210        &self.built_by
211    }
212}
213
214/// Holds various type of Buildable
215#[derive(Clone, Debug)]
216pub enum BuildableObject {
217    /// Wrap a container
218    Container(BuiltByContainer),
219    /// Wrap a task id
220    Id(TaskId),
221    /// Wrap any other type
222    Other(Arc<dyn Buildable>),
223    /// Represents a buildable with no task dependencies
224    None,
225}
226
227impl BuildableObject {
228    /// Create a buildable object from something that can be turned into a buildable
229    pub fn new<B: IntoBuildable>(buildable: B) -> Self
230    where
231        <B as IntoBuildable>::Buildable: 'static,
232    {
233        Self::Other(Arc::new(buildable.into_buildable()))
234    }
235}
236
237impl Buildable for BuildableObject {
238    fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
239        match self {
240            BuildableObject::Container(c) => c.get_dependencies(project),
241            BuildableObject::Id(id) => id.get_dependencies(project),
242            BuildableObject::Other(o) => o.get_dependencies(project),
243            BuildableObject::None => Ok(HashSet::new()),
244        }
245    }
246}
247
248impl From<BuiltByContainer> for BuildableObject {
249    fn from(c: BuiltByContainer) -> Self {
250        BuildableObject::Container(c)
251    }
252}
253
254impl From<TaskId> for BuildableObject {
255    fn from(c: TaskId) -> Self {
256        BuildableObject::Id(c)
257    }
258}
259
260impl From<Box<dyn Buildable>> for BuildableObject {
261    fn from(boxed: Box<dyn Buildable>) -> Self {
262        let arc = Arc::from(boxed);
263        BuildableObject::Other(arc)
264    }
265}
266
267assert_impl_all!(BuildableObject: Buildable, IntoBuildable, GetBuildable);