build_pretty/
lib.rs

1use retrieve::mod_pub_use;
2use std::process::Command;
3
4rust_i18n::i18n!("locales");
5
6/// This macro is the Build Script version of println!
7/// Special thank to (@Ryan1729)[https://github.com/rust-lang/cargo/issues/985#issuecomment-1071667472]
8#[macro_export]
9macro_rules! cargo_warning_ln { ($($tokens: tt)*) => { println!("cargo:warning={}", format!($($tokens)*)); } }
10
11/// This macro can be used to automatically set CARGO_PKG_NAME and CARGO_PKG_VERSION.
12#[macro_export]
13macro_rules! build_pretty {
14 ($($tokens:tt)*) => {
15  build_pretty::BuildPretty::new().with_cargo_pkg_name(env!("CARGO_PKG_NAME")).with_cargo_pkg_version(env!("CARGO_PKG_VERSION"))
16 };
17}
18
19#[mod_pub_use(build_pretty_error, task, task_fn, build_type_string, command_builder)]
20pub struct BuildPretty
21{
22 /// [ ( task_name, task ), ... ]
23 /// Helper methods are available:
24 ///  for command: `queue_command`, `queue_commands`
25 ///  for fn     : `queue_fn`     , `queue_fns`
26 pub tasks: Vec<(&'static str, Task)>,
27
28 /// true  => panic! if task was failed. (default)
29 /// false => run all tasks and return results.
30 pub safe_panic: bool,
31
32 /// Replacement value of "{cargo_pkg_name}" in a message.
33 /// see also: `build_pretty!` macro.
34 pub cargo_pkg_name: Option<&'static str>,
35
36 /// Replacement value of "{cargo_pkg_name}" in a message.
37 /// see also: `build_pretty!` macro.
38 pub cargo_pkg_version: Option<&'static str>,
39
40 /// Notice about `warnings` that are displayed on the left side by `Cargo:warning`. Set to None if not needed.
41 pub message_warning_notice: Option<String>,
42
43 /// Template for a message announcing the start of the build process. Set to None if not needed.
44 pub message_process_begin: Option<String>,
45 /// Template for a message announcing the end of the build process. Set to None if not needed.
46 pub message_process_end:   Option<String>,
47
48 /// Template for a message announcing the start of the task. Set to None if not needed.
49 pub message_task_begin:        Option<String>,
50 /// Template for a message from the task output. Set to None if not needed.
51 pub message_task_body:         Option<String>,
52 /// Template for a message announcing a succeeded of the task. Set to None if not needed.
53 pub message_task_succeeded:    Option<String>,
54 /// Template for a message announcing an error of the task. Set to None if not needed.
55 pub message_task_error:        Option<String>,
56 /// Template for a message announcing notice of an error of the task. Set to None if not needed.
57 pub message_task_error_notice: Option<String>,
58
59 /// Template for the safe panic message. Set to None if not needed.
60 pub message_safe_panic: Option<String>
61}
62
63impl BuildPretty
64{
65 pub fn new() -> Self
66 {
67  use rust_i18n::t;
68  Self {
69   tasks:                     vec![],
70   safe_panic:                true,
71   cargo_pkg_name:            None,
72   cargo_pkg_version:         None,
73   message_warning_notice:    Some(t!("message_warning_notice")),
74   message_process_begin:     Some(t!("message_process_begin")),
75   message_process_end:       Some(t!("message_process_end")),
76   message_task_begin:        Some(t!("message_task_begin")),
77   message_task_body:         Some(t!("message_task_body")),
78   message_task_succeeded:    Some(t!("message_task_succeeded")),
79   message_task_error:        Some(t!("message_task_error")),
80   message_task_error_notice: Some(t!("message_task_error_notice")),
81   message_safe_panic:        Some(t!("message_safe_panic"))
82  }
83 }
84
85 pub fn with_cargo_pkg_name(mut self, v: &'static str) -> Self
86 {
87  self.cargo_pkg_name = Some(v);
88  self
89 }
90
91 pub fn with_cargo_pkg_version(mut self, v: &'static str) -> Self
92 {
93  self.cargo_pkg_version = Some(v);
94  self
95 }
96
97 pub fn new_with_locale(locale: &str) -> Self
98 {
99  rust_i18n::set_locale(locale);
100  Self::new()
101 }
102
103 pub fn enque_command(&mut self, task_name: &'static str, command: Command) -> &mut Self
104 {
105  self.tasks.push((task_name, Task::from(command)));
106  self
107 }
108
109 pub fn enque_commands(&mut self, task_name_and_commands: Vec<(&'static str, Command)>) -> &mut Self
110 {
111  for (task_name, command) in task_name_and_commands.into_iter()
112  {
113   let task = Task::from(command);
114   self.tasks.push((task_name, task));
115  }
116  self
117 }
118
119 pub fn enque_fn(&mut self, task_name: &'static str, fun: TaskFn) -> &mut Self
120 {
121  self.tasks.push((task_name, Task::from(fun)));
122  self
123 }
124
125 pub fn enque_fns(&mut self, task_name_and_funs: Vec<(&'static str, TaskFn)>) -> &mut Self
126 {
127  for (task_name, fun) in task_name_and_funs.into_iter()
128  {
129   let task = Task::from(fun);
130   self.tasks.push((task_name, task));
131  }
132  self
133 }
134
135 pub fn invoke(&mut self) -> Vec<Result<String, BuildPrettyError>>
136 {
137  let mut rr = vec![];
138  let total_tasks_string = self.tasks.len().to_string();
139  put_message_process(
140   &self.message_process_begin,
141   &total_tasks_string,
142   self.cargo_pkg_name.unwrap_or_default(),
143   self.cargo_pkg_version.unwrap_or_default()
144  );
145  put_message_process(
146   &self.message_warning_notice,
147   &total_tasks_string,
148   self.cargo_pkg_name.unwrap_or_default(),
149   self.cargo_pkg_version.unwrap_or_default()
150  );
151  for (task_index, (task_name, task)) in self.tasks.iter_mut().enumerate()
152  {
153   let task_index_string = (task_index + 1).to_string();
154   put_message_task(
155    &self.message_task_begin,
156    task_name,
157    &task_index_string,
158    &total_tasks_string,
159    self.cargo_pkg_name.unwrap_or_default(),
160    self.cargo_pkg_version.unwrap_or_default()
161   );
162   let r = task.invoke(task_name, task_index);
163   match r.as_ref()
164   {
165    Ok(o) if self.message_task_body.is_some() =>
166    {
167     put_message_task_body(
168      &self.message_task_body,
169      &o,
170      task_name,
171      &task_index.to_string(),
172      &total_tasks_string,
173      self.cargo_pkg_name.unwrap_or_default(),
174      self.cargo_pkg_version.unwrap_or_default()
175     )
176    },
177    Err(e) =>
178    {
179     put_message_task_body(
180      &self.message_task_body,
181      &e.extract_output(),
182      task_name,
183      &task_index_string,
184      &total_tasks_string,
185      self.cargo_pkg_name.unwrap_or_default(),
186      self.cargo_pkg_version.unwrap_or_default()
187     );
188     put_message_task(
189      &self.message_task_error,
190      task_name,
191      &task_index_string,
192      &total_tasks_string,
193      self.cargo_pkg_name.unwrap_or_default(),
194      self.cargo_pkg_version.unwrap_or_default()
195     );
196     put_message_task(
197      &self.message_task_error_notice,
198      task_name,
199      &task_index_string,
200      &total_tasks_string,
201      self.cargo_pkg_name.unwrap_or_default(),
202      self.cargo_pkg_version.unwrap_or_default()
203     );
204     let m = resolve_message_safe_panic(
205      &self.message_safe_panic,
206      task_name,
207      &task_index_string,
208      &total_tasks_string,
209      self.cargo_pkg_name.unwrap_or_default(),
210      self.cargo_pkg_version.unwrap_or_default()
211     );
212     self.tasks.clear();
213     panic!("{m:?}")
214    },
215    _ =>
216    {}
217   }
218   put_message_task(
219    &self.message_task_succeeded,
220    task_name,
221    &task_index_string,
222    &total_tasks_string,
223    self.cargo_pkg_name.unwrap_or_default(),
224    self.cargo_pkg_version.unwrap_or_default()
225   );
226   rr.push(r)
227  }
228  put_message_process(
229   &self.message_process_end,
230   &total_tasks_string,
231   self.cargo_pkg_name.unwrap_or_default(),
232   self.cargo_pkg_version.unwrap_or_default()
233  );
234  self.tasks.clear();
235  rr
236 }
237}
238
239impl Drop for BuildPretty
240{
241 fn drop(&mut self)
242 {
243  if !self.tasks.is_empty()
244  {
245   self.invoke();
246  }
247 }
248}
249
250fn resolve_message_template(
251 template: &String,
252 task_name: &str,
253 task_index: &str,
254 total_tasks: &str,
255 task_message: &str,
256 cargo_pkg_name: &str,
257 cargo_pkg_version: &str
258) -> String
259{
260 template
261  .replace("{cargo_pkg_name}", cargo_pkg_name)
262  .replace("{cargo_pkg_version}", cargo_pkg_version)
263  .replace("{build_type}", BUILD_TYPE_STRING)
264  .replace("{task_name}", task_name)
265  .replace("{task_index}", task_index)
266  .replace("{total_tasks}", total_tasks)
267  .replace("{task_message}", task_message)
268}
269
270fn put_message_process(template: &Option<String>, total_tasks: &str, cargo_pkg_name: &str, cargo_pkg_version: &str)
271{
272 if let Some(template) = template
273 {
274  let m = resolve_message_template(template, "", "", total_tasks, "", cargo_pkg_name, cargo_pkg_version);
275  cargo_warning_ln!("{m}");
276 }
277}
278
279fn put_message_task(
280 template: &Option<String>,
281 task_name: &str,
282 task_index: &str,
283 total_tasks: &str,
284 cargo_pkg_name: &str,
285 cargo_pkg_version: &str
286)
287{
288 if let Some(template) = template
289 {
290  let m = resolve_message_template(template, task_name, task_index, total_tasks, "", cargo_pkg_name, cargo_pkg_version);
291  cargo_warning_ln!("{m}");
292 }
293}
294
295fn put_message_task_body(
296 template: &Option<String>,
297 message: &str,
298 task_name: &str,
299 task_index: &str,
300 total_tasks: &str,
301 cargo_pkg_name: &str,
302 cargo_pkg_version: &str
303)
304{
305 if let Some(template) = template
306 {
307  message.split('\n').into_iter().filter(|m| !m.is_empty()).for_each(|m| {
308   let m = resolve_message_template(template, task_name, task_index, total_tasks, m, cargo_pkg_name, cargo_pkg_version);
309   cargo_warning_ln!("{m}");
310  });
311 }
312}
313
314fn resolve_message_safe_panic(
315 template: &Option<String>,
316 task_name: &str,
317 task_index: &str,
318 total_tasks: &str,
319 cargo_pkg_name: &str,
320 cargo_pkg_version: &str
321) -> String
322{
323 template
324  .as_ref()
325  .map(|template| resolve_message_template(template, task_name, task_index, total_tasks, "", cargo_pkg_name, cargo_pkg_version))
326  .unwrap_or_default()
327}