1use retrieve::mod_pub_use;
2use std::process::Command;
3
4rust_i18n::i18n!("locales");
5
6#[macro_export]
9macro_rules! cargo_warning_ln { ($($tokens: tt)*) => { println!("cargo:warning={}", format!($($tokens)*)); } }
10
11#[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 pub tasks: Vec<(&'static str, Task)>,
27
28 pub safe_panic: bool,
31
32 pub cargo_pkg_name: Option<&'static str>,
35
36 pub cargo_pkg_version: Option<&'static str>,
39
40 pub message_warning_notice: Option<String>,
42
43 pub message_process_begin: Option<String>,
45 pub message_process_end: Option<String>,
47
48 pub message_task_begin: Option<String>,
50 pub message_task_body: Option<String>,
52 pub message_task_succeeded: Option<String>,
54 pub message_task_error: Option<String>,
56 pub message_task_error_notice: Option<String>,
58
59 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}