1use crate::logging::{ConsoleMode, LoggingArgs};
4use crate::plugins::PluginManager;
5use crate::prelude::listeners::TaskExecutionGraphListener;
6use crate::prelude::{PluginAware, SettingsAware};
7use std::backtrace::Backtrace;
8
9use crate::project::ProjectResult;
10use crate::startup::execution_graph::ExecutionGraph;
11use crate::startup::listeners::{BuildListener, Listener, TaskExecutionListener};
12use crate::version::{version, Version};
13
14use itertools::Itertools;
15use log::Level;
16use once_cell::sync::OnceCell;
17use parking_lot::RwLock;
18use std::collections::HashMap;
19use std::env::current_dir;
20use std::fmt::Debug;
21use std::ops::{Deref, DerefMut};
22use std::path::{Path, PathBuf};
23use std::sync::Arc;
24
25#[derive(Debug)]
27pub struct Assemble {
28 plugins: PluginManager<Assemble>,
29 task_listeners: Vec<Box<dyn TaskExecutionListener>>,
30 task_graph_listeners: Vec<Box<dyn TaskExecutionGraphListener>>,
31 build_listeners: Vec<Box<dyn BuildListener>>,
32 version: Version,
33 start_parameter: StartParameter,
34 graph: RwLock<OnceCell<ExecutionGraph>>,
35}
36
37impl Assemble {
38 pub fn new(start: StartParameter) -> Self {
40 Self {
41 plugins: PluginManager::new(),
42 task_listeners: vec![],
43 task_graph_listeners: vec![],
44 build_listeners: vec![],
45 version: version(),
46 start_parameter: start,
47 graph: Default::default(),
48 }
49 }
50
51 pub fn set_execution_graph(&mut self, graph: &ExecutionGraph) -> ProjectResult {
53 self.graph
54 .write()
55 .set(graph.clone())
56 .expect("execution graph already set");
57 for listener in &mut self.task_graph_listeners {
58 listener.graph_ready(graph)?;
59 }
60 Ok(())
61 }
62
63 pub fn add_listener<T: Listener<Listened = Self>>(&mut self, listener: T) -> ProjectResult {
65 listener.add_listener(self)
66 }
67
68 pub fn add_task_execution_listener<T: TaskExecutionListener + 'static>(
69 &mut self,
70 listener: T,
71 ) -> ProjectResult {
72 self.task_listeners.push(Box::new(listener));
73 Ok(())
74 }
75
76 pub fn add_task_execution_graph_listener<T: TaskExecutionGraphListener + 'static>(
77 &mut self,
78 mut listener: T,
79 ) -> ProjectResult {
80 if let Some(graph) = self.graph.read().get() {
81 listener.graph_ready(graph)
82 } else {
83 self.task_graph_listeners.push(Box::new(listener));
84 Ok(())
85 }
86 }
87
88 pub fn add_build_listener<T: BuildListener + 'static>(&mut self, listener: T) -> ProjectResult {
89 self.build_listeners.push(Box::new(listener));
90 Ok(())
91 }
92
93 pub fn settings_evaluated<S: SettingsAware>(&mut self, settings: S) -> ProjectResult {
94 trace!("running settings evaluated method in build listeners");
95 settings.with_settings(|settings| {
96 self.build_listeners
97 .iter_mut()
98 .map(|b| b.settings_evaluated(&settings))
99 .collect::<ProjectResult>()
100 })
101 }
102
103 pub fn assemble_version(&self) -> &Version {
105 &self.version
106 }
107
108 pub fn start_parameter(&self) -> &StartParameter {
110 &self.start_parameter
111 }
112 pub fn current_dir(&self) -> &Path {
113 self.start_parameter().current_dir()
114 }
115
116 pub fn project_dir(&self) -> PathBuf {
117 self.start_parameter().project_dir()
118 }
119
120 pub fn properties(&self) -> &HashMap<String, Option<String>> {
121 &self.start_parameter.properties
122 }
123}
124
125impl PluginAware for Assemble {
126 fn plugin_manager(&self) -> &PluginManager<Self> {
127 &self.plugins
128 }
129
130 fn plugin_manager_mut(&mut self) -> &mut PluginManager<Self> {
131 &mut self.plugins
132 }
133}
134
135impl Default for Assemble {
136 fn default() -> Self {
137 Assemble::new(StartParameter::new())
138 }
139}
140
141pub trait AssembleAware {
143 fn with_assemble<F, R>(&self, func: F) -> R
145 where
146 F: FnOnce(&Assemble) -> R;
147
148 fn with_assemble_mut<F, R>(&mut self, func: F) -> R
150 where
151 F: FnOnce(&mut Assemble) -> R;
152
153 fn start_parameter(&self) -> StartParameter {
154 self.with_assemble(|asm| asm.start_parameter.clone())
155 }
156}
157
158impl AssembleAware for Assemble {
159 fn with_assemble<F, R>(&self, func: F) -> R
161 where
162 F: FnOnce(&Assemble) -> R,
163 {
164 (func)(self)
165 }
166
167 fn with_assemble_mut<F, R>(&mut self, func: F) -> R
168 where
169 F: FnOnce(&mut Assemble) -> R,
170 {
171 (func)(self)
172 }
173}
174
175impl AssembleAware for Arc<RwLock<Assemble>> {
176 fn with_assemble<F, R>(&self, func: F) -> R
177 where
178 F: FnOnce(&Assemble) -> R,
179 {
180 (func)(self.read().deref())
181 }
182
183 fn with_assemble_mut<F, R>(&mut self, func: F) -> R
184 where
185 F: FnOnce(&mut Assemble) -> R,
186 {
187 (func)(self.write().deref_mut())
188 }
189}
190
191#[derive(Debug, Clone)]
195pub struct StartParameter {
196 current_dir: PathBuf,
197 logging: LoggingArgs,
198 mode: ConsoleMode,
199 project_dir: Option<PathBuf>,
200 properties: HashMap<String, Option<String>>,
201 task_requests: Vec<String>,
202 workers: usize,
203 backtrace: BacktraceEmit,
204 rerun_tasks: bool,
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209pub enum BacktraceEmit {
210 None,
211 Short,
212 Long,
213}
214
215impl BacktraceEmit {
216 pub fn emit(&self, level: Level, backtrace: &Backtrace) {
217 let lines: Vec<String> = match self {
218 BacktraceEmit::None => return,
219 BacktraceEmit::Short => {
220 let mut bt = backtrace
221 .to_string()
222 .lines()
223 .skip_while(|line| !line.contains("assemble_core::error::PayloadError"))
224 .map(|s| s.to_string())
225 .collect::<Vec<_>>();
226
227 if let Some(pos) = bt
228 .iter()
229 .position(|p| p.contains("assemble::main"))
230 .map(|s| s + 2)
231 {
232 bt.drain(pos..);
233 }
234
235 bt.into_iter()
236 .tuples::<(_, _)>()
237 .map(|(frame, location)| {
238 if location.contains("/rustc/") {
239 vec!["\t... <hidden>".to_string()]
240 } else {
241 vec![frame, location]
242 }
243 })
244 .flatten()
245 .fold(vec![], |mut acc, line| {
246 let should_push = if line.trim().starts_with("...") {
247 if let Some(last) = acc.last() {
248 if !last.trim().starts_with("...") {
249 true
250 } else {
251 false
252 }
253 } else {
254 true
255 }
256 } else {
257 true
258 };
259 if should_push {
260 acc.push(line);
261 }
262 acc
263 })
264 }
265 BacktraceEmit::Long => backtrace
266 .to_string()
267 .lines()
268 .map(|s| s.to_string())
269 .collect(),
270 };
271
272 for line in lines {
273 log!(level, "{}", line);
274 }
275 }
276}
277
278impl StartParameter {
279 pub fn new() -> Self {
281 Self {
282 current_dir: current_dir().expect("no valid current working directory"),
283 logging: LoggingArgs::default(),
284 mode: ConsoleMode::Auto,
285 project_dir: None,
286 properties: HashMap::new(),
287 task_requests: vec![],
288 workers: 0,
289 backtrace: BacktraceEmit::None,
290 rerun_tasks: false,
291 }
292 }
293
294 pub fn current_dir(&self) -> &Path {
297 &self.current_dir
298 }
299
300 pub fn mode(&self) -> ConsoleMode {
302 self.mode
303 }
304
305 pub fn project_dir(&self) -> PathBuf {
308 self.project_dir
309 .as_ref()
310 .unwrap_or(&self.current_dir)
311 .clone()
312 }
313
314 pub fn properties(&self) -> &HashMap<String, Option<String>> {
316 &self.properties
317 }
318
319 pub fn properties_mut(&mut self) -> &mut HashMap<String, Option<String>> {
321 &mut self.properties
322 }
323
324 pub fn backtrace(&self) -> BacktraceEmit {
326 self.backtrace
327 }
328
329 pub fn task_requests(&self) -> &[String] {
332 &self.task_requests
333 }
334
335 pub fn task_requests_mut(&mut self) -> &mut Vec<String> {
338 &mut self.task_requests
339 }
340
341 pub fn with_task_requests<S: AsRef<str>, I: IntoIterator<Item = S>>(mut self, iter: I) -> Self {
343 self.task_requests_mut()
344 .extend(iter.into_iter().map(|s| s.as_ref().to_string()));
345 self
346 }
347
348 pub fn is_rerun_tasks(&self) -> bool {
350 self.rerun_tasks
351 }
352
353 pub fn rerun_tasks(&mut self) {
355 self.rerun_tasks = true;
356 }
357
358 pub fn set_current_dir<P: AsRef<Path>>(&mut self, current_dir: P) {
360 self.current_dir = current_dir.as_ref().to_path_buf();
361 }
362 pub fn set_logging(&mut self, log_level: LoggingArgs) {
364 self.logging = log_level;
365 }
366
367 pub fn set_mode(&mut self, mode: ConsoleMode) {
369 self.mode = mode;
370 }
371
372 pub fn set_project_dir<P: AsRef<Path>>(&mut self, project_dir: P) {
374 self.project_dir = Some(project_dir.as_ref().to_path_buf());
375 }
376
377 pub fn set_backtrace(&mut self, backtrace: BacktraceEmit) {
378 self.backtrace = backtrace;
379 }
380
381 pub fn workers(&self) -> usize {
382 self.workers
383 }
384
385 pub fn set_workers(&mut self, workers: usize) {
386 self.workers = workers;
387 }
388 pub fn logging(&self) -> &LoggingArgs {
389 &self.logging
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396
397 #[test]
398 fn get_assemble_version() {
399 let assemble = Assemble::default();
400 println!("assemble: {:#?}", assemble);
401 assert_eq!(assemble.assemble_version(), &version());
402 }
403}