assemble_core/project/
requests.rs

1//! Turns a list of strings into a task request object
2
3use crate::error::PayloadError;
4use crate::identifier::TaskId;
5use crate::project::error::{ProjectError, ProjectResult};
6use crate::project::finder::{ProjectFinder, ProjectPathBuf, TaskFinder, TaskPath, TaskPathBuf};
7use crate::project::shared::SharedProject;
8use crate::task::flags::{OptionsSlurper, WeakOptionsDecoder};
9use std::collections::{HashMap, VecDeque};
10
11/// The finalized tasks requests.
12#[derive(Debug)]
13pub struct TaskRequests {
14    task_to_weak_decoder: HashMap<TaskId, usize>,
15    weak_decoders: Vec<WeakOptionsDecoder>,
16    tasks: Vec<TaskId>,
17}
18
19impl TaskRequests {
20    /// Build a task requests object from a given project and an arbitrary list of strings that contain
21    /// only tasks, options, and values for said options if required.
22    pub fn build<I: IntoIterator<Item = S>, S: AsRef<TaskPath>>(
23        project: &SharedProject,
24        args: I,
25    ) -> ProjectResult<Self> {
26        let mut builder = TaskRequestsBuilder::new();
27        let mut reqs: VecDeque<TaskPathBuf> =
28            VecDeque::from_iter(args.into_iter().map(|s| s.as_ref().to_owned()));
29
30        if reqs.is_empty() {
31            // first run, add defaults if they exist.
32            project.with(|p| {
33                reqs.extend(p.default_tasks().iter().map(|s| s.clone().into()));
34            })
35        }
36
37        if reqs.is_empty() {
38            return Ok(builder.finish());
39        }
40
41        let task_finder = TaskFinder::new(project);
42        let proj_finder = ProjectFinder::new(project);
43
44        while let Some(task) = reqs.pop_front() {
45            let task_req: &TaskPath = task.as_ref();
46            debug!("attempting to find tasks for task path {:?}", task_req);
47
48            let ids: Option<Vec<TaskId>> = task_finder.find(task_req)?;
49
50            if let Some(ids) = ids {
51                let first = ids.first().unwrap();
52
53                let project = proj_finder
54                    .find(ProjectPathBuf::from(first.project_id().unwrap()))
55                    .unwrap();
56
57                let mut any_handle = project.get_task(first)?;
58                let resolved = any_handle.resolve_shared(&project)?;
59
60                if let Some(ops) = resolved.options_declarations() {
61                    let slurper = OptionsSlurper::new(&ops);
62                    let slice = reqs.make_contiguous();
63                    let (weak, count) = slurper.slurp(slice).map_err(PayloadError::new)?;
64                    builder.add_configured_tasks(ids, weak);
65                    reqs.drain(..count);
66                } else {
67                    builder.add_tasks(ids);
68                }
69            } else {
70                return Err(ProjectError::NoIdentifiersFound(task_req.to_string()).into());
71            }
72        }
73
74        Ok(builder.finish())
75    }
76
77    /// Get the decoder for a given task id
78    pub fn decoder(&self, id: &TaskId) -> Option<WeakOptionsDecoder> {
79        let index = self.task_to_weak_decoder.get(id)?;
80        Some(self.weak_decoders[*index].clone())
81    }
82
83    /// Tasks requests, in-order
84    pub fn requested_tasks(&self) -> &[TaskId] {
85        &self.tasks[..]
86    }
87}
88
89struct TaskRequestsBuilder {
90    in_progress: TaskRequests,
91}
92
93impl TaskRequestsBuilder {
94    fn new() -> Self {
95        Self {
96            in_progress: TaskRequests {
97                task_to_weak_decoder: Default::default(),
98                weak_decoders: vec![],
99                tasks: vec![],
100            },
101        }
102    }
103
104    fn finish(self) -> TaskRequests {
105        self.in_progress
106    }
107
108    fn add_tasks<I>(&mut self, tasks: I)
109    where
110        I: IntoIterator<Item = TaskId>,
111    {
112        self.in_progress.tasks.extend(tasks)
113    }
114
115    fn add_configured_tasks<I>(&mut self, tasks: I, decoder: WeakOptionsDecoder)
116    where
117        I: IntoIterator<Item = TaskId>,
118    {
119        let index = self.in_progress.weak_decoders.len();
120        self.in_progress.weak_decoders.push(decoder);
121        for task in tasks {
122            self.in_progress.tasks.push(task.clone());
123            self.in_progress.task_to_weak_decoder.insert(task, index);
124        }
125    }
126}