1use std::any::type_name;
2use std::collections::HashSet;
3use std::fmt::{Debug, Formatter};
4use std::marker::PhantomData;
5use std::sync::{Arc, Mutex};
6
7use crate::defaults::tasks::Empty;
8use crate::error::PayloadError;
9use crate::exception::BuildException;
10use crate::identifier::{InvalidId, TaskId};
11use crate::immutable::Immutable;
12use crate::lazy_evaluation::{Provider, ProviderError};
13use crate::project::buildable::{Buildable, IntoBuildable};
14use crate::project::error::{ProjectError, ProjectResult};
15use crate::project::shared::SharedProject;
16use crate::project::shared::WeakSharedProject;
17use crate::task::flags::{OptionDeclarations, OptionsDecoder};
18use crate::task::up_to_date::UpToDate;
19use crate::task::{BuildableTask, FullTask, HasTaskId, TaskOrdering};
20use crate::{BuildResult, Executable, Project};
21
22use super::ExecutableTask;
23use super::Task;
24
25pub struct ConfigureTask<T: Task> {
26 func: Box<dyn FnOnce(&mut Executable<T>, &Project) -> ProjectResult + Send>,
27}
28
29impl<T: Task> Debug for ConfigureTask<T> {
30 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31 write!(f, "<configure task type <{}>>", type_name::<T>())
32 }
33}
34
35impl<T: Task> ConfigureTask<T> {
36 pub fn configure(self, task: &mut Executable<T>, project: &Project) -> ProjectResult {
37 (self.func)(task, project)
38 }
39}
40
41pub trait ResolveTask {
42 type Executable: ExecutableTask;
43
44 fn resolve_task(self, project: &SharedProject) -> ProjectResult<Self::Executable>;
45}
46
47pub trait ResolveInnerTask: Send {
48 fn resolve_task(&mut self, project: &SharedProject) -> ProjectResult<()>;
49}
50
51assert_obj_safe!(ResolveInnerTask);
52
53#[derive(Debug)]
54pub struct LazyTask<T: Task + Send + Debug + 'static> {
55 task_type: PhantomData<T>,
56 task_id: Immutable<TaskId>,
57 configurations: Vec<ConfigureTask<T>>,
58 shared: Option<WeakSharedProject>,
59}
60
61impl<T: Task + Send + Debug + 'static> LazyTask<T> {
62 fn new(id: TaskId, shared: &WeakSharedProject) -> Self {
63 Self {
64 task_type: PhantomData,
65 task_id: Immutable::new(id),
66 configurations: vec![],
67 shared: Some(shared.clone()),
68 }
69 }
70
71 fn empty() -> Self {
72 Self {
73 task_type: PhantomData,
74 task_id: Immutable::default(),
75 configurations: vec![],
76 shared: None,
77 }
78 }
79}
80
81impl<T: Task + Send + Sync + Debug + 'static> ResolveTask for LazyTask<T> {
82 type Executable = Executable<T>;
83
84 fn resolve_task(self, project: &SharedProject) -> ProjectResult<Executable<T>> {
85 trace!("Resolving task {}", self.task_id.as_ref());
86 let task = project.with(|project| T::new(self.task_id.as_ref(), project))?;
87 let mut executable = Executable::new(
88 self.shared
89 .unwrap()
90 .upgrade()
91 .expect("could not upgrade project"),
92 task,
93 self.task_id,
94 );
95
96 project.with(|project| executable.initialize(project))?;
97 executable.configure_io()?;
98
99 for config in self.configurations {
100 project.with(|project| config.configure(&mut executable, project))?;
101 }
102
103 Ok(executable)
104 }
105}
106
107#[derive(Debug)]
108enum TaskHandleInner<T: Task + Send + Sync + Debug + 'static> {
109 Lazy(LazyTask<T>),
110 Configured(Executable<T>),
111}
112
113impl<T: Task + Send + Sync + Debug + 'static> TaskHandleInner<T> {
114 fn bare_resolve(&mut self) -> ProjectResult<()> {
115 let project = match self {
116 TaskHandleInner::Lazy(l) => l
117 .shared
118 .as_ref()
119 .ok_or(ProjectError::NoSharedProjectSet)?
120 .clone(),
121 TaskHandleInner::Configured(_) => {
122 return Ok(());
123 }
124 };
125 let project = project.upgrade()?;
126 self.resolve(&project)
127 }
128
129 fn resolve(&mut self, project: &SharedProject) -> ProjectResult<()> {
130 let configured: Executable<T> = match self {
131 TaskHandleInner::Lazy(lazy) => {
132 let lazy = std::mem::replace(lazy, LazyTask::empty());
133 lazy.resolve_task(project)?
134 }
135 TaskHandleInner::Configured { .. } => return Ok(()),
136 };
137 *self = TaskHandleInner::Configured(configured);
138 Ok(())
139 }
140
141 fn bare_configured(&mut self) -> ProjectResult<&Executable<T>> {
142 self.bare_resolve()?;
143 if let TaskHandleInner::Configured(exec) = &*self {
144 Ok(exec)
145 } else {
146 panic!("task should be configured")
147 }
148 }
149
150 fn bare_configured_mut(&mut self) -> ProjectResult<&mut Executable<T>> {
151 self.bare_resolve()?;
152 if let TaskHandleInner::Configured(exec) = &mut *self {
153 Ok(exec)
154 } else {
155 panic!("task should be configured")
156 }
157 }
158
159 fn configured(&mut self, project: &SharedProject) -> ProjectResult<&Executable<T>> {
160 self.resolve(project)?;
161 if let TaskHandleInner::Configured(exec) = &*self {
162 Ok(exec)
163 } else {
164 panic!("task should be configured")
165 }
166 }
167
168 fn configured_mut(&mut self, project: &SharedProject) -> ProjectResult<&mut Executable<T>> {
169 self.resolve(project)?;
170 if let TaskHandleInner::Configured(exec) = &mut *self {
171 Ok(exec)
172 } else {
173 panic!("task should be configured")
174 }
175 }
176}
177
178impl<T: Task + Send + Sync + Debug + 'static> ResolveInnerTask for TaskHandleInner<T> {
179 fn resolve_task(&mut self, project: &SharedProject) -> ProjectResult<()> {
180 self.resolve(project)
181 }
182}
183
184pub struct TaskHandle<T: Task + Send + Sync + Debug + 'static> {
185 id: TaskId,
186 connection: Arc<Mutex<TaskHandleInner<T>>>,
187}
188
189impl<T: Task + Send + Sync + Debug + 'static> UpToDate for TaskHandle<T> {
190 fn up_to_date(&self) -> bool {
191 let mut guard = {
192 if let Ok(guard) = self.connection.lock() {
193 guard
194 } else {
195 return false;
196 }
197 };
198 if let Ok(configured) = guard.bare_configured() {
199 configured.task_up_to_date()
200 } else {
201 false
202 }
203 }
204}
205
206impl<T: Task + Send + Sync + Debug + 'static> TaskHandle<T> {
207 pub fn id(&self) -> &TaskId {
209 &self.id
210 }
211
212 pub fn configure_with<F>(&mut self, config: F) -> ProjectResult
213 where
214 F: FnOnce(&mut Executable<T>, &Project) -> ProjectResult + Send + 'static,
215 {
216 let mut guard = self.connection.lock().map_err(PayloadError::new)?;
217 match &mut *guard {
218 TaskHandleInner::Lazy(lazy) => {
219 lazy.configurations.push(ConfigureTask {
220 func: Box::new(config),
221 });
222 }
223 TaskHandleInner::Configured(c) => {
224 let shared_project = c.project();
225 shared_project.with(|project| (config)(c, project))?;
226 }
227 }
228 Ok(())
229 }
230
231 fn configured<R, F: FnOnce(&Executable<T>) -> R>(&self, func: F) -> ProjectResult<R> {
232 Ok((func)(
233 self.connection
234 .lock()
235 .map_err(PayloadError::new)?
236 .bare_configured()?,
237 ))
238 }
239
240 pub fn provides<F, R>(&self, func: F) -> TaskProvider<T, R, F>
241 where
242 F: Fn(&Executable<T>) -> R + Send + Sync,
243 R: Clone + Send + Sync,
244 {
245 TaskProvider::new(self.clone(), func)
246 }
247}
248
249assert_impl_all!(TaskHandle<Empty>: Sync);
250
251impl<T: Task + Send + Sync + Debug + 'static> Debug for TaskHandle<T> {
252 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
253 if f.alternate() {
254 f.debug_struct("TaskHandle")
255 .field("type", &type_name::<T>())
256 .field("id", &self.id)
257 .finish()
258 } else {
259 write!(f, "{:?}", self.id)
260 }
261 }
262}
263
264impl<T: Task + Send + Sync + Debug + 'static> Buildable for TaskHandle<T> {
265 fn get_dependencies(&self, project: &Project) -> ProjectResult<HashSet<TaskId>> {
266 let mut guard = self.connection.lock().map_err(PayloadError::new)?;
267 let configured = guard.configured(&project.as_shared())?;
268 configured.into_buildable().get_dependencies(project)
269 }
270}
271
272impl<T: Task + Send + Sync + Debug + 'static> HasTaskId for TaskHandle<T> {
273 fn task_id(&self) -> TaskId {
274 self.id.clone()
275 }
276}
277
278impl<T: Task + Send + Sync + Debug + 'static> BuildableTask for TaskHandle<T> {
279 fn ordering(&self) -> Vec<TaskOrdering> {
280 let mut guard = self.connection.lock().unwrap();
281 guard
282 .bare_configured()
283 .expect("could not get configured")
284 .ordering()
285 }
286}
287
288impl<T: Task + Send + Sync + Debug + 'static> Clone for TaskHandle<T> {
289 fn clone(&self) -> Self {
290 Self {
291 id: self.id.clone(),
292 connection: self.connection.clone(),
293 }
294 }
295}
296
297impl<T: Task + Send + Sync + Debug + 'static> ResolveInnerTask for TaskHandle<T> {
298 fn resolve_task(&mut self, project: &SharedProject) -> ProjectResult<()> {
299 self.connection
300 .lock()
301 .map_err(PayloadError::new)?
302 .resolve_task(project)
303 }
304}
305impl<T: Task + Send + Sync + Debug + 'static> ExecutableTask for TaskHandle<T> {
306 fn options_declarations(&self) -> Option<OptionDeclarations> {
307 let mut guard = self.connection.lock().unwrap();
308 guard.bare_configured().unwrap().options_declarations()
309 }
310
311 fn try_set_from_decoder(&mut self, decoder: &OptionsDecoder) -> ProjectResult<()> {
312 let mut guard = self
313 .connection
314 .lock()
315 .map_err(|_| ProjectError::PoisonError)?;
316 guard
317 .bare_configured_mut()
318 .unwrap()
319 .try_set_from_decoder(decoder)
320 }
321
322 fn execute(&mut self, project: &Project) -> BuildResult {
323 let mut guard = self
324 .connection
325 .lock()
326 .map_err(|_| BuildException::new("Could not get access to provider"))?;
327 guard.configured_mut(&project.as_shared())?.execute(project)
328 }
329
330 fn did_work(&self) -> bool {
331 let mut guard = self
332 .connection
333 .lock()
334 .map_err(|_| BuildException::new("Could not get access to provider"))
335 .unwrap();
336 guard.bare_configured().unwrap().did_work()
337 }
338
339 fn task_up_to_date(&self) -> bool {
340 let mut guard = self
341 .connection
342 .lock()
343 .map_err(|_| BuildException::new("Could not get access to provider"))
344 .unwrap();
345 guard.bare_configured().unwrap().task_up_to_date()
346 }
347
348 fn group(&self) -> String {
349 self.configured(|e| e.group()).unwrap()
350 }
351
352 fn description(&self) -> String {
353 self.configured(|e| e.description()).unwrap()
354 }
355}
356
357pub trait ResolveExecutable: ResolveInnerTask {
358 fn get_executable(&mut self, project: &SharedProject) -> ProjectResult<Box<dyn FullTask>>;
359}
360
361impl<T: Task + Send + Sync + Debug + 'static> ResolveExecutable for TaskHandle<T> {
362 fn get_executable(&mut self, project: &SharedProject) -> ProjectResult<Box<dyn FullTask>> {
363 self.resolve_task(project)?;
364 Ok(Box::new(self.clone()))
365 }
366}
367
368pub struct TaskProvider<T, R, F>
369where
370 T: Task + Send + Sync + Debug + 'static,
371 F: Fn(&Executable<T>) -> R + Send + Sync,
372 R: Clone + Send + Sync,
373{
374 handle: TaskHandle<T>,
375 lift: F,
376}
377
378impl<T, R, F> Clone for TaskProvider<T, R, F>
379where
380 T: Task + Send + Sync + Debug + 'static,
381 F: Fn(&Executable<T>) -> R + Send + Sync + Clone,
382 R: Clone + Send + Sync,
383{
384 fn clone(&self) -> Self {
385 Self {
386 handle: self.handle.clone(),
387 lift: self.lift.clone(),
388 }
389 }
390}
391
392impl<T, F, R> Buildable for TaskProvider<T, R, F>
393where
394 F: Fn(&Executable<T>) -> R + Send + Sync,
395 R: Clone + Send + Sync,
396 T: 'static + Debug + Send + Task + Sync,
397{
398 fn get_dependencies(&self, _: &Project) -> ProjectResult<HashSet<TaskId>> {
399 Ok(HashSet::from_iter([self.handle.id.clone()]))
400 }
401}
402
403impl<T, F, R> Debug for TaskProvider<T, R, F>
404where
405 F: Fn(&Executable<T>) -> R + Send + Sync,
406 R: Clone + Send + Sync,
407 T: 'static + Debug + Send + Task + Sync,
408{
409 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
410 f.debug_struct("TaskProvider")
411 .field("handle", &self.handle)
412 .finish_non_exhaustive()
413 }
414}
415
416impl<T, F, R> Provider<R> for TaskProvider<T, R, F>
417where
418 T: Task + Send + Sync + Debug + 'static,
419 F: Fn(&Executable<T>) -> R + Send + Sync,
420 R: Clone + Send + Sync,
421{
422 fn missing_message(&self) -> String {
423 format!("couldn't get a value from task {}", self.handle.id)
424 }
425
426 fn try_get(&self) -> Option<R> {
427 let mut guard = self.handle.connection.lock().expect("Could not get inner");
428 let configured = guard.bare_configured().expect("could not configure task");
429 Some((self.lift)(configured))
430 }
431
432 fn fallible_get(&self) -> Result<R, ProviderError> {
433 let mut guard = self
434 .handle
435 .connection
436 .lock()
437 .map_err(|e| ProviderError::new(e.to_string()))?;
438 let configured = guard
439 .bare_configured()
440 .map_err(|e| ProviderError::new(e.to_string()))?;
441 Ok((self.lift)(configured))
442 }
443}
444
445impl<T, F, R> TaskProvider<T, R, F>
446where
447 T: Task + Send + Sync + Debug + 'static,
448 F: Fn(&Executable<T>) -> R + Send + Sync,
449 R: Clone + Send + Sync,
450{
451 pub fn new(handle: TaskHandle<T>, lift: F) -> Self {
452 Self { handle, lift }
453 }
454}
455
456#[derive(Debug)]
457pub struct TaskHandleFactory {
458 project: WeakSharedProject,
459}
460
461impl TaskHandleFactory {
462 pub(crate) fn new(project: WeakSharedProject) -> Self {
463 Self { project }
464 }
465
466 pub fn create_handle<T: Task + Send + Sync + Debug + 'static>(
468 &self,
469 id: TaskId,
470 ) -> Result<TaskHandle<T>, InvalidId> {
471 let lazy = LazyTask::<T>::new(id.clone(), &self.project);
472 let inner = TaskHandleInner::Lazy(lazy);
473 Ok(TaskHandle {
474 id,
475 connection: Arc::new(Mutex::new(inner)),
476 })
477 }
478}
479
480#[cfg(test)]
481mod tests {
482
483 #[test]
484 fn lazy_create_task() {}
485}