assemble_core/project/
shared.rs

1//! Contains the shared project
2
3use crate::__export::{ProjectError, ProjectResult, TaskId};
4use crate::dependencies::dependency_container::ConfigurationHandler;
5use crate::dependencies::RegistryContainer;
6use crate::error::PayloadError;
7use crate::identifier::{ProjectId, TaskIdFactory};
8use crate::plugins::PluginAware;
9use crate::project::finder::{TaskFinder, TaskPath};
10use crate::project::GetProjectId;
11use crate::task::task_container::TaskContainer;
12use crate::task::{AnyTaskHandle, TaskHandle};
13use crate::{project, Plugin, Project, Task, Workspace};
14use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
15use std::fmt::{Debug, Display, Formatter};
16use std::ops::{Deref, DerefMut};
17use std::sync::{Arc, Weak};
18
19/// The shared project allows for many projects to share references to the same
20/// [`Project`](Project) instance.
21#[derive(Debug, Clone)]
22pub struct SharedProject(Arc<(TrueSharableProject, ProjectId)>);
23
24pub(crate) type TrueSharableProject = RwLock<Project>;
25
26impl SharedProject {
27    pub(super) fn new_cyclic<F: FnOnce(&WeakSharedProject) -> Project>(func: F) -> Self {
28        let modified = |weak: &Weak<(TrueSharableProject, ProjectId)>| {
29            let weakened = WeakSharedProject(weak.clone());
30            let project = func(&weakened);
31            let id = project.id().clone();
32            (RwLock::new(project), id)
33        };
34        let arc = Arc::new_cyclic(modified);
35        Self(arc)
36    }
37
38    fn new(project: Project) -> Self {
39        let id = project.id().clone();
40        Self(Arc::new((RwLock::new(project), id)))
41    }
42
43    pub(crate) fn weak(&self) -> WeakSharedProject {
44        WeakSharedProject(Arc::downgrade(&self.0))
45    }
46
47    pub fn with<F, R>(&self, func: F) -> R
48    where
49        F: FnOnce(&Project) -> R,
50    {
51        let guard = self
52            .0
53             .0
54            .try_read()
55            .unwrap_or_else(|| panic!("Couldn't get read access to {}", self));
56        let r = (func)(&*guard);
57        r
58    }
59
60    pub fn with_mut<F, R>(&self, func: F) -> R
61    where
62        F: FnOnce(&mut Project) -> R,
63    {
64        let mut guard = self
65            .0
66             .0
67            .try_write()
68            .unwrap_or_else(|| panic!("Couldn't get read access to {}", self));
69        let r = (func)(&mut *guard);
70        r
71    }
72
73    pub fn guard<'g, T, F: Fn(&Project) -> &T + 'g>(&'g self, func: F) -> ProjectResult<Guard<T>> {
74        let guard = match self.0 .0.try_read() {
75            Some(guard) => guard,
76            None => {
77                panic!("Accessing this immutable guard would block for {}", self)
78            }
79        };
80        Ok(Guard::new(guard, func))
81    }
82
83    pub fn guard_mut<'g, T, F1, F2>(
84        &'g self,
85        ref_getter: F1,
86        mut_getter: F2,
87    ) -> ProjectResult<GuardMut<T>>
88    where
89        F1: Fn(&Project) -> &T + 'g,
90        F2: Fn(&mut Project) -> &mut T + 'g,
91    {
92        let guard = match self.0 .0.try_write() {
93            Some(guard) => guard,
94            None => {
95                panic!("Accessing this guard would block for {}", self)
96            }
97        };
98        Ok(GuardMut::new(guard, ref_getter, mut_getter))
99    }
100
101    pub fn tasks(&self) -> GuardMut<TaskContainer> {
102        self.guard_mut(
103            |project| project.task_container(),
104            |project| project.task_container_mut(),
105        )
106        .expect("couldn't safely get task container")
107    }
108
109    pub fn register_task<T: Task + Send + Sync + Debug + 'static>(
110        &self,
111        id: &str,
112    ) -> ProjectResult<TaskHandle<T>> {
113        self.tasks().with_mut(|t| t.register_task::<T>(id))
114    }
115
116    /// Gets a task with a given name
117    pub fn get_task(&self, id: &TaskId) -> ProjectResult<AnyTaskHandle> {
118        Ok(self.task_container().with(|t| {
119            t.get_task(id)
120                .cloned()
121                .ok_or(ProjectError::IdentifierMissing(id.clone()))
122        })?)
123    }
124
125    /// Gets a typed task
126    pub fn get_typed_task<T: Task + Send + Sync>(
127        &self,
128        id: &TaskId,
129    ) -> ProjectResult<TaskHandle<T>> {
130        self.get_task(id).and_then(|id| {
131            id.as_type::<T>()
132                .ok_or(ProjectError::custom("invalid task type").into())
133        })
134    }
135
136    /// Finds a task, using this project as the base task
137    pub fn find_task<P: AsRef<TaskPath>>(&self, path: P) -> ProjectResult<AnyTaskHandle> {
138        let finder = TaskFinder::new(self);
139        let path = path.as_ref();
140        let ids = finder
141            .find(path)?
142            .ok_or(ProjectError::TaskNotFound(path.to_owned()))?;
143
144        match &ids[..] {
145            [] => unreachable!(),
146            [task_id] => self.get_task(task_id),
147            [..] => Err(PayloadError::new(ProjectError::TooManyIdentifiersFound(
148                ids,
149                path.to_string(),
150            ))),
151        }
152    }
153
154    pub fn get_subproject<P>(&self, project: P) -> project::Result<SharedProject>
155    where
156        P: AsRef<str>,
157    {
158        self.with(|p| p.get_subproject(project).cloned())
159    }
160
161    /// Executes a callback on this project and all sub projects
162    pub fn allprojects<F: Fn(&Project) + Clone>(&self, callback: F) {
163        self.with(callback.clone());
164        self.with(|s| {
165            s.subprojects()
166                .iter()
167                .for_each(|&project| project.allprojects(callback.clone()))
168        })
169    }
170
171    /// Executes a callback on this project and all sub projects
172    pub fn allprojects_mut<F: Fn(&mut Project) + Clone>(&self, callback: F) {
173        self.with_mut(callback.clone());
174        self.with(|s| {
175            s.subprojects()
176                .iter()
177                .for_each(|&project| project.allprojects_mut(callback.clone()))
178        })
179    }
180
181    /// Gets the task container for this project
182    pub fn task_container(&self) -> Guard<TaskContainer> {
183        self.guard(|project| project.task_container())
184            .expect("couldn't safely get task container")
185    }
186
187    pub(crate) fn task_id_factory(&self) -> Guard<TaskIdFactory> {
188        self.guard(|project| project.task_id_factory())
189            .expect("couldn't safely get task id factory")
190    }
191
192    /// Get access to the registries container
193    pub fn registries<F, R>(&self, func: F) -> R
194    where
195        F: FnOnce(&mut RegistryContainer) -> R,
196    {
197        self.with(|r| {
198            let mut registries_lock = r.registries.lock().unwrap();
199            let registries = &mut *registries_lock;
200            func(registries)
201        })
202    }
203
204    /// Get a guard to the dependency container within the project
205    pub fn configurations_mut(&mut self) -> GuardMut<ConfigurationHandler> {
206        self.guard_mut(
207            |project| project.configurations(),
208            |project| project.configurations_mut(),
209        )
210        .expect("couldn't safely get dependencies container")
211    }
212
213    /// Get a guard to the dependency container within the project
214    pub fn configurations(&self) -> Guard<ConfigurationHandler> {
215        self.guard(|project| project.configurations())
216            .expect("couldn't safely get dependencies container")
217    }
218
219    pub fn workspace(&self) -> Guard<Workspace> {
220        self.guard(|p| &p.workspace)
221            .expect("couldn't get workspace")
222    }
223
224    /// Apply a plugin to this.
225    pub fn apply_plugin<P: Plugin<Project>>(&self) -> ProjectResult {
226        self.with_mut(|p| p.apply_plugin::<P>())
227    }
228}
229
230impl Display for SharedProject {
231    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
232        write!(f, "{}", self.0 .1)
233    }
234}
235
236impl Default for SharedProject {
237    fn default() -> Self {
238        Project::new().unwrap()
239    }
240}
241
242impl TryFrom<Weak<(TrueSharableProject, ProjectId)>> for SharedProject {
243    type Error = ();
244
245    fn try_from(
246        value: Weak<(TrueSharableProject, ProjectId)>,
247    ) -> std::result::Result<Self, Self::Error> {
248        let arc = value.upgrade().ok_or(())?;
249        Ok(SharedProject(arc))
250    }
251}
252
253impl TryFrom<WeakSharedProject> for SharedProject {
254    type Error = PayloadError<ProjectError>;
255
256    fn try_from(value: WeakSharedProject) -> std::result::Result<Self, Self::Error> {
257        value.upgrade()
258    }
259}
260
261impl From<Arc<(TrueSharableProject, ProjectId)>> for SharedProject {
262    fn from(arc: Arc<(TrueSharableProject, ProjectId)>) -> Self {
263        Self(arc)
264    }
265}
266
267/// Provides a shortcut around the project
268pub struct Guard<'g, T> {
269    guard: RwLockReadGuard<'g, Project>,
270    getter: Box<dyn Fn(&Project) -> &T + 'g>,
271}
272
273impl<'g, T> Guard<'g, T> {
274    pub fn new<F>(guard: RwLockReadGuard<'g, Project>, getter: F) -> Self
275    where
276        F: Fn(&Project) -> &T + 'g,
277    {
278        Self {
279            guard,
280            getter: Box::new(getter),
281        }
282    }
283
284    pub fn with<R, F: FnOnce(&T) -> R>(&self, func: F) -> R {
285        let guard = &*self.guard;
286        let t = (self.getter)(guard);
287        let r = (func)(t);
288        r
289    }
290}
291
292impl<T> Deref for Guard<'_, T> {
293    type Target = T;
294
295    fn deref(&self) -> &Self::Target {
296        let guard = &*self.guard;
297        (self.getter)(guard)
298    }
299}
300
301/// Provides a shortcut around the project
302pub struct GuardMut<'g, T> {
303    guard: RwLockWriteGuard<'g, Project>,
304    ref_getter: Box<dyn Fn(&Project) -> &T + 'g>,
305    mut_getter: Box<dyn Fn(&mut Project) -> &mut T + 'g>,
306}
307
308impl<'g, T> GuardMut<'g, T> {
309    pub fn new<F1, F2>(guard: RwLockWriteGuard<'g, Project>, ref_getter: F1, mut_getter: F2) -> Self
310    where
311        F1: Fn(&Project) -> &T + 'g,
312        F2: Fn(&mut Project) -> &mut T + 'g,
313    {
314        Self {
315            guard,
316            ref_getter: Box::new(ref_getter),
317            mut_getter: Box::new(mut_getter),
318        }
319    }
320
321    pub fn with<R, F: FnOnce(&T) -> R>(&self, func: F) -> R {
322        let guard = &*self.guard;
323        let t = (self.ref_getter)(guard);
324        let r = (func)(t);
325        r
326    }
327
328    pub fn with_mut<R, F: FnOnce(&mut T) -> R>(&mut self, func: F) -> R {
329        let guard = &mut *self.guard;
330        let t = (self.mut_getter)(guard);
331        let r = (func)(t);
332        r
333    }
334}
335
336impl<T> Deref for GuardMut<'_, T> {
337    type Target = T;
338
339    fn deref(&self) -> &Self::Target {
340        let guard = &*self.guard;
341        (self.ref_getter)(guard)
342    }
343}
344
345impl<T> DerefMut for GuardMut<'_, T> {
346    fn deref_mut(&mut self) -> &mut Self::Target {
347        let ref mut guard = *self.guard;
348        (self.mut_getter)(guard)
349    }
350}
351
352impl GetProjectId for SharedProject {
353    fn project_id(&self) -> ProjectId {
354        self.with(|p| p.project_id())
355    }
356
357    fn parent_id(&self) -> Option<ProjectId> {
358        self.with(GetProjectId::parent_id)
359    }
360
361    fn root_id(&self) -> ProjectId {
362        self.with(GetProjectId::root_id)
363    }
364}
365
366#[derive(Debug, Clone)]
367pub(crate) struct WeakSharedProject(Weak<(TrueSharableProject, ProjectId)>);
368
369impl WeakSharedProject {
370    /// Upgrades a weakly shared project
371    pub fn upgrade(self) -> project::Result<SharedProject> {
372        self.0
373            .upgrade()
374            .map(SharedProject)
375            .ok_or(ProjectError::NoSharedProjectSet.into())
376    }
377}