1use std::{
2 env,
3 result,
4 io::ErrorKind,
5 time::SystemTime,
6 default::Default,
7 hash::{Hash, Hasher},
8 collections::VecDeque,
9 path::{Path, PathBuf},
10 fmt::{Display, Formatter},
11 process::{exit, Command, Output, Stdio, Child},
12 fs::{rename, metadata, read_dir, remove_file, create_dir_all, remove_dir_all}
13};
14
15pub const C_COMPILER: &str = if cfg!(feature = "gcc") {"gcc"}
16else if cfg!(feature = "clang") {"clang"}
17else if cfg!(feature = "mingw32") {"x86_64-w64-mingw32-gcc"}
18else if cfg!(windows) {"cl.exe"}
19else {"cc"};
20
21pub const CXX_COMPILER: &str = if cfg!(feature = "gxx") {"g++"}
22else if cfg!(feature = "clangxx") {"clang++"}
23else if cfg!(feature = "mingw32") {"x86_64-w64-mingw32-g++"}
24else if cfg!(windows) {"cl.exe"}
25else {"c++"};
26
27pub const CC: &str = C_COMPILER;
28pub const CXXC: &str = CXX_COMPILER;
29
30pub const DELIM: &str = if cfg!(windows) {"\\"} else {"/"};
31pub const DELIM_CHAR: char = if cfg!(windows) {'\\'} else {'/'};
32
33pub const CMD_ARG: &str = if cfg!(windows) {"cmd"} else {"sh"};
34pub const CMD_ARG2: &str = if cfg!(windows) {"/C"} else {"-c"};
35
36pub const MOVE_ACP_PTR_SYMBOL: &str = ".n";
41pub const MAP: &str = MOVE_ACP_PTR_SYMBOL;
42
43pub type IoError = std::io::Error;
44pub type IoResult<T> = std::io::Result::<T>;
45
46#[macro_export]
50macro_rules! go_rebuild_yourself {
51 () => {{
52 let source_path = file!();
53 Rob::go_rebuild_yourself(&source_path).unwrap();
54 }};
55 (?) => {{
56 let source_path = file!();
57 Rob::go_rebuild_yourself(&source_path)?;
58 }}
59}
60
61#[macro_export]
73macro_rules! log {
74 ($log_level: tt, $($args: tt)*) => {{
75 #[allow(unused)]
76 use LogLevel::*;
77 Rob::log($log_level, &format!($($args)*));
78 }}
79}
80
81#[macro_export]
84macro_rules! pathbuf {
85 ($($p: expr), *) => {{
86 let mut path = std::path::PathBuf::new();
87 $(path.push($p);)*
88 path
89 }}
90}
91
92#[macro_export]
95macro_rules! path {
96 ($($p: expr), *) => {{
97 let path = [$($p), *];
98 path.join(DELIM)
99 }}
100}
101
102#[macro_export]
110macro_rules! mkdirs {
111 ($($dir: expr), *) => {{
112 let p = pathbuf![$($dir), *];
113 Rob::mkdir(p)
114 }}
115}
116
117macro_rules! colored {
118 ($f: expr, r.$str: expr) => { write!($f, "\x1b[91m{}\x1b[0m", $str) };
119 ($f: expr, y.$str: expr) => { write!($f, "\x1b[93m{}\x1b[0m", $str) };
120 ($f: expr, br.$str: expr) => { write!($f, "\x1b[31m{}\x1b[0m", $str) };
121}
122
123#[derive(Debug)]
125pub struct DirRec {
126 stack: VecDeque::<PathBuf>,
127}
128
129impl DirRec {
130 pub fn new<P>(root: P) -> DirRec
138 where
139 P: Into::<PathBuf>
140 {
141 let mut stack = VecDeque::new();
142 stack.push_back(root.into());
143 DirRec {stack}
144 }
145}
146
147impl Iterator for DirRec {
148 type Item = PathBuf;
149
150 fn next(&mut self) -> Option<Self::Item> {
151 while let Some(p) = self.stack.pop_front() {
152 if p.is_file() { return Some(p) }
153
154 match read_dir(&p) {
155 Ok(es) => es.filter_map(Result::ok).for_each(|e| {
156 self.stack.push_back(e.path())
157 }),
158 Err(e) => eprintln!("ERROR: {e}")
159 }
160 } None
161 }
162}
163
164#[derive(Debug)]
166pub struct Dir {
167 paths: Vec::<PathBuf>,
168}
169
170impl Dir {
171 pub fn new<P>(root: P) -> Dir
179 where
180 P: Into::<PathBuf>
181 {
182 Dir {
183 paths: if let Ok(entries) = read_dir(root.into()) {
184 entries.into_iter()
185 .filter_map(Result::ok)
186 .filter(|e| e.path().is_file())
187 .map(|e| e.path())
188 .collect()
189 } else {
190 Vec::new()
191 }
192 }
193 }
194}
195
196impl IntoIterator for Dir {
197 type Item = PathBuf;
198 type IntoIter = std::vec::IntoIter<Self::Item>;
199
200 fn into_iter(self) -> Self::IntoIter {
201 self.paths.into_iter()
202 }
203}
204
205#[derive(Debug)]
206pub enum LogLevel {
207 CMD,
208 INFO,
209 WARN,
210 ERROR,
211 PANIC
212}
213
214impl Display for LogLevel {
215 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
216 use self::LogLevel::*;
217 match self {
218 CMD => write!(f, "[CMD]")?,
219 INFO => write!(f, "[INFO]")?,
220 WARN => colored!(f, y."[WARN]")?,
221 ERROR => colored!(f, br."[ERROR]")?,
222 PANIC => colored!(f, r."[PANIC]")?
223 } Ok(())
224 }
225}
226
227#[derive(Eq, Debug, Clone, PartialEq)]
229pub struct RobCommand {
230 lines: Vec::<Vec::<String>>,
231 acp: usize, ecp: usize, pub cfg: Config,
234 output_stack: VecDeque::<Output>,
235}
236
237impl Hash for RobCommand {
238 fn hash<H: Hasher>(&self, state: &mut H) {
239 self.lines.hash(state);
240 self.acp.hash(state);
241 self.ecp.hash(state);
242 self.cfg.hash(state);
243 }
244}
245
246impl From<Config> for RobCommand {
247 fn from(cfg: Config) -> Self {
248 Self {
249 cfg, ..Self::default()
250 }
251 }
252}
253
254impl From<Vec::<Vec::<String>>> for RobCommand {
255 fn from(lines: Vec::<Vec::<String>>) -> Self {
256 Self {
257 lines,
258 ..Self::default()
259 }
260 }
261}
262
263impl Default for RobCommand {
264 fn default() -> Self {
265 Self {
266 lines: vec![Vec::new()],
267 acp: 0, ecp: 0,
268 cfg: Config::default(),
269 output_stack: VecDeque::new()
270 }
271 }
272}
273
274impl RobCommand {
275 pub fn new() -> RobCommand {
276 RobCommand::default()
277 }
278
279 pub fn append<S>(&mut self, xs: &[S]) -> &mut Self
291 where
292 S: ToString + PartialEq::<&'static str>
293 {
294 if matches!(xs.last(), Some(last) if last == &MAP) {
296 let args = xs[0..xs.len() - 1].into_iter().map(S::to_string).collect::<Vec::<_>>();
297 self.lines[self.acp].extend(args);
298 self.move_acp_ptr();
299 } else {
300 let args = xs.into_iter().map(S::to_string).collect::<Vec::<_>>();
301 self.lines[self.acp].extend(args);
302 } self
303 }
304
305 pub fn append_mv<S>(&mut self, xs: &[S]) -> &mut Self
307 where
308 S: ToString + PartialEq::<&'static str>
309 {
310 self.append(xs);
311 self.move_acp_ptr();
312 self
313 }
314
315 #[inline]
316 pub fn move_acp_ptr(&mut self) -> &mut Self {
317 self.acp += 1;
318 self.lines.push(Vec::new());
319 self
320 }
321
322 pub fn execute(&mut self) -> IoResult::<&mut Self> {
323 let out = self.execute_sync()?;
324 if let Some(last) = self.lines.last_mut() {
325 *last = Vec::new();
326 }
327 self.output_stack.push_back(out);
328 Ok(self)
329 }
330
331 #[inline]
332 pub fn execute_sync(&mut self) -> IoResult::<Output> {
333 self.execute_sync_exit(true)
334 }
335
336 #[inline]
337 pub fn execute_sync_dont_exit(&mut self) -> IoResult::<Output> {
338 self.execute_sync_exit(false)
339 }
340
341 fn execute_sync_exit(&mut self, exit_: bool) -> IoResult::<Output> {
342 let Some(args) = self.get_args()
343 else {
344 let err = IoError::new(ErrorKind::Other, "No arguments to process");
345 return Err(err)
346 };
347
348 if self.cfg.echo { log!(CMD, "{args}"); }
349 let mut cmd = Command::new(CMD_ARG);
350 cmd.arg(CMD_ARG2).arg(args);
351
352 if !self.cfg.echo {
353 cmd.stdout(Stdio::null())
354 .stderr(Stdio::null());
355 }
356
357 let out = cmd.output()?;
358
359 if !self.cfg.keepgoing && !out.status.success() {
360 let code = out.status.code()
361 .expect("Process terminated by signal");
362
363 let stderr = String::from_utf8_lossy(&out.stderr);
364 log!(ERROR, "{stderr}");
365 log!(ERROR, "Compilation exited abnormally with code: {code}");
366
367 if exit_ {
368 exit(1);
369 }
370 }
371 Ok(out)
372 }
373
374 #[inline]
375 pub fn execute_all_sync(&mut self) -> RobResult::<Vec::<Output>> {
376 self.execute_all_sync_exit(true)
377 }
378
379 #[inline]
380 pub fn execute_all_sync_dont_exit(&mut self) -> RobResult::<Vec::<Output>> {
381 self.execute_all_sync_exit(false)
382 }
383
384 pub fn execute_all_sync_exit(&mut self, exit_: bool) -> RobResult::<Vec::<Output>> {
386 let mut outs = Vec::new();
387 for idx in self.ecp..self.lines.len() {
388 let line = &self.lines[idx];
389 let args = line.join(" ");
390 if args.is_empty() { continue }
391
392 if self.cfg.echo { log!(CMD, "{args}"); }
393 let mut cmd = Command::new(CMD_ARG);
394 cmd.arg(CMD_ARG2).arg(args);
395
396 if !self.cfg.echo {
397 cmd.stdout(Stdio::null())
398 .stderr(Stdio::null());
399 }
400
401 let out = cmd.output().map_err(|err| {
402 RobError::FailedToGetOutput(err)
403 })?;
404
405 if !self.cfg.keepgoing && !out.status.success() {
406 let code = out.status.code()
407 .expect("Process terminated by signal");
408
409 let stderr = String::from_utf8_lossy(&out.stderr);
410 log!(ERROR, "{stderr}");
411 log!(ERROR, "Compilation exited abnormally with code: {code}");
412
413 if exit_ {
414 exit(1);
415 }
416 }
417
418 self.ecp += 1;
419 outs.push(out);
420 }
421 Ok(outs)
422 }
423
424 #[inline]
425 fn get_args(&self) -> Option::<String> {
426 if let Some(args) = self.lines.last() {
427 if args.is_empty() { None }
428 else { Some(args.join(" ")) }
429 } else { None }
430 }
431
432 #[inline]
465 pub fn output(&mut self) -> Option::<Output> {
466 self.output_stack.pop_front()
467 }
468
469 #[inline]
470 pub fn outputs_refs(&self) -> VecDeque::<&Output> {
471 self.output_stack.iter().collect()
472 }
473
474 #[inline]
475 pub fn outputs(self) -> VecDeque::<Output> {
476 self.output_stack.into_iter().collect()
477 }
478
479 pub fn execute_all_async(&mut self) -> RobResult::<Vec::<Child>> {
481 let mut children = Vec::new();
482 for idx in self.ecp..self.lines.len() {
483 let line = &self.lines[idx];
484 let args = line.join(" ");
485 if args.is_empty() { continue }
486
487 if self.cfg.echo { log!(CMD, "{args}"); }
488 let mut cmd = Command::new(CMD_ARG);
489 cmd.arg(CMD_ARG2).arg(&args);
490
491 let child = Command::new(CMD_ARG)
492 .arg(CMD_ARG2)
493 .arg(args)
494 .stdout(Stdio::piped())
495 .stderr(Stdio::piped())
496 .spawn()
497 .map_err(|err| RobError::FailedToSpawnChild(err))?;
498
499 self.ecp += 1;
500 children.push(child);
501 }
502 Ok(children)
503 }
504
505 fn format_out(out: &str) -> &str {
506 if out.ends_with('\n') {
507 &out[0..out.len() - 1]
508 } else {
509 &out[0..]
510 }
511 }
512
513 #[inline]
514 pub fn execute_all_async_and_wait(&mut self) -> RobResult::<Vec::<Output>> {
515 self.execute_all_async_and_wait_exit(true)
516 }
517
518 #[inline]
519 pub fn execute_all_async_and_wait_dont_exit(&mut self) -> RobResult::<Vec::<Output>> {
520 self.execute_all_async_and_wait_exit(false)
521 }
522
523 pub fn execute_all_async_and_wait_exit(&mut self, exit: bool) -> RobResult::<Vec::<Output>> {
525 let children = self.execute_all_async()?;
526 Self::wait_for_children_deq(children.into(), &self.cfg, exit)
527 }
528
529 pub fn wait_for_children_deq(mut children: VecDeque::<Child>, cfg: &Config, exit_: bool) -> RobResult::<Vec::<Output>> {
531 let mut ret = Vec::new();
532 while let Some(child) = children.pop_front() {
533 let out = Self::wait_for_child(child).map_err(|err| {
534 RobError::FailedToGetOutput(err)
535 })?;
536
537 if out.status.success() {
538 let stdout = String::from_utf8_lossy(&out.stdout);
539 if !stdout.is_empty() && cfg.echo {
540 let formatted = Self::format_out(&stdout);
541 log!(INFO, "{formatted}");
542 }
543 } else {
544 let stderr = String::from_utf8_lossy(&out.stderr);
545 if !stderr.is_empty() && cfg.echo {
546 let formatted = Self::format_out(&stderr);
547 log!(ERROR, "{formatted}");
548 if !cfg.keepgoing {
549 let code = out.status.code().expect("Process terminated by signal");
550 log!(ERROR, "Compilation exited abnormally with code: {code}");
551 if exit_ {
552 exit(1);
553 }
554 }
555 }
556 }
557
558 ret.push(out);
559 } Ok(ret)
560 }
561
562 #[inline]
564 pub fn wait_for_child(child: Child) -> IoResult::<Output> {
565 child.wait_with_output()
566 }
567
568 #[inline]
569 pub fn echo(&mut self, echo: bool) -> &mut Self {
570 self.cfg.echo(echo);
571 self
572 }
573
574 #[inline]
575 pub fn keepgoing(&mut self, keepgoing: bool) -> &mut Self {
576 self.cfg.keepgoing(keepgoing);
577 self
578 }
579}
580
581#[derive(Hash, Eq, Debug, Clone, PartialEq)]
582pub struct Config {
583 pub echo: bool,
584 pub keepgoing: bool,
585}
586
587impl Default for Config {
588 fn default() -> Self {
589 Self{echo: true, keepgoing: false}
590 }
591}
592
593impl Config {
594 #[inline]
595 pub fn echo(&mut self, echo: bool) -> &mut Self {
596 self.echo = echo;
597 self
598 }
599
600 #[inline]
601 pub fn keepgoing(&mut self, keepgoing: bool) -> &mut Self {
602 self.keepgoing = keepgoing;
603 self
604 }
605}
606
607#[derive(Hash, Eq, Debug, Clone, Default, PartialEq)]
608pub struct Job {
609 target: String,
610 phony: bool,
611 deps: Vec::<String>,
612 cmd: RobCommand,
613 reusable_cmd: bool
614}
615
616#[derive(Debug)]
617pub enum RobError {
618 NotFound(String),
619 FailedToGetOutput(IoError),
620 FailedToSpawnChild(IoError)
621}
622
623impl Display for RobError {
624 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
625 use RobError::*;
626 match self {
627 NotFound(file_path) => write!(f, "File not found: {file_path}"),
628 FailedToGetOutput(err) => write!(f, "Failed to get output: {err}", err = err.to_string()),
629 FailedToSpawnChild(err) => write!(f, "Failed to spawn child: {err}", err = err.to_string()),
630 }
631 }
632}
633
634pub type RobResult<T> = result::Result::<T, RobError>;
635
636impl Job {
637 pub fn new<S>(target: &str, deps: Vec::<S>, cmd: RobCommand) -> Job
638 where
639 S: Into::<String>
640 {
641 let target = target.to_owned();
642 let deps = deps.into_iter().map(Into::into).collect::<Vec::<_>>();
643 Job {target, deps, cmd, ..Self::default()}
644 }
645
646 #[inline(always)]
647 pub fn target(&self) -> &String {
648 &self.target
649 }
650
651 #[inline(always)]
652 pub fn deps(&self) -> &Vec::<String> {
653 &self.deps
654 }
655
656 #[inline(always)]
657 pub fn cmd(&self) -> &RobCommand {
658 &self.cmd
659 }
660
661 #[inline(always)]
662 pub fn cfg(&self) -> &Config {
663 &self.cmd.cfg
664 }
665
666 #[inline(always)]
667 pub fn cfg_mut(&mut self) -> &mut Config {
668 &mut self.cmd.cfg
669 }
670
671 #[inline(always)]
672 pub fn reusable_cmd(&mut self, reusable_cmd: bool) -> &mut Self {
673 self.reusable_cmd = reusable_cmd;
674 self
675 }
676
677 #[inline(always)]
678 pub fn phony(&mut self, phony: bool) -> &mut Self {
679 self.phony = phony;
680 self
681 }
682
683 #[inline(always)]
684 pub fn keepgoing(&mut self, keepgoing: bool) -> &mut Self {
685 self.cmd.keepgoing(keepgoing);
686 self
687 }
688
689 #[inline(always)]
690 pub fn echo(&mut self, echo: bool) -> &mut Self {
691 self.cmd.echo(echo);
692 self
693 }
694
695 #[inline]
696 pub fn needs_rebuild(&self) -> RobResult::<bool> {
697 if self.phony {
698 Ok(true)
699 } else {
700 Rob::needs_rebuild_many(&self.target, &self.deps)
701 }
702 }
703
704 fn execute(&mut self, sync: bool, exit_: bool, check: bool) -> RobResult::<Vec::<Output>> {
705 if !check || self.needs_rebuild()? {
706 let cmd = if self.reusable_cmd {
707 &mut self.cmd.clone()
708 } else {
709 &mut self.cmd
710 };
711 if sync {
712 return if exit_ {
713 cmd.execute_all_sync()
714 } else {
715 cmd.execute_all_sync_dont_exit()
716 }
717 } else {
718 return if exit_ {
719 cmd.execute_all_async_and_wait()
720 } else {
721 cmd.execute_all_async_and_wait_dont_exit()
722 }
723 }
724 } else {
725 log!(INFO, "Nothing to be done for '{target}'.", target = self.target);
726 Ok(Vec::new())
727 }
728 }
729
730 #[inline(always)]
731 pub fn execute_async_dont_exit(&mut self) -> RobResult::<Vec::<Output>> {
732 self.execute(false, false, true)
733 }
734
735 #[inline(always)]
736 pub fn execute_async(&mut self) -> RobResult::<Vec::<Output>> {
737 self.execute(false, true, true)
738 }
739
740 #[inline(always)]
741 pub fn execute_sync_dont_exit(&mut self) -> RobResult::<Vec::<Output>> {
742 self.execute(true, false, true)
743 }
744
745 #[inline(always)]
746 pub fn execute_sync(&mut self) -> RobResult::<Vec::<Output>> {
747 self.execute(true, true, true)
748 }
749
750 #[inline(always)]
751 pub fn execute_async_dont_exit_unchecked(&mut self) -> RobResult::<Vec::<Output>> {
752 self.execute(false, false, false)
753 }
754
755 #[inline(always)]
756 pub fn execute_async_unchecked(&mut self) -> RobResult::<Vec::<Output>> {
757 self.execute(false, true, false)
758 }
759
760 #[inline(always)]
761 pub fn execute_sync_dont_exit_unchecked(&mut self) -> RobResult::<Vec::<Output>> {
762 self.execute(true, false, false)
763 }
764
765 #[inline(always)]
766 pub fn execute_sync_unchecked(&mut self) -> RobResult::<Vec::<Output>> {
767 self.execute(true, true, false)
768 }
769}
770
771#[derive(Hash, Eq, Debug, Clone, Default, PartialEq)]
773pub struct Rob {
774 pub cfg: Config,
775 cmd: RobCommand,
776 jobs: Vec::<Job>,
777}
778
779impl Rob {
780 pub const MAX_DIR_LVL: usize = 4;
781
782 pub fn new() -> Rob {
783 Rob::default()
784 }
785
786 pub fn needs_rebuild(bin: &str, src: &str) -> IoResult::<bool> {
788 if !Rob::path_exists(bin) { return Ok(true) }
789 let bin_mod_time = Rob::get_last_modification_time(bin).map_err(|err| {
790 log!(ERROR, "{err}: {bin}"); err
791 })?;
792 let src_mod_time = Rob::get_last_modification_time(src).map_err(|err| {
793 log!(ERROR, "{err}: {bin}"); err
794 })?;
795 Ok(src_mod_time > bin_mod_time)
796 }
797
798 pub fn needs_rebuild_many(bin: &str, srcs: &Vec::<String>) -> RobResult::<bool> {
799 let mut times = Vec::new();
803 for src in srcs.iter() {
804 match Rob::get_last_modification_time(src) {
805 Ok(time) => times.push(time),
806 Err(_) => return Err(RobError::NotFound(src.to_owned()))
807 }
808 }
809
810 if !Rob::path_exists(bin) { return Ok(true) }
811
812 let bin_mod_time = match Rob::get_last_modification_time(bin) {
813 Ok(time) => time,
814 Err(_) => return Err(RobError::NotFound(bin.to_owned()))
815 };
816
817 Ok(times.into_iter().any(|src_mod_time| src_mod_time > bin_mod_time))
818 }
819
820 pub fn go_rebuild_yourself(source_path: &str) -> IoResult::<()> {
841 let args = env::args().collect::<Vec::<_>>();
842 assert!(args.len() >= 1);
843 let binary_pathbuf = std::env::current_exe()?;
844 let binary_path = binary_pathbuf.to_str().to_owned().unwrap();
845 let rebuild_is_needed = Rob::needs_rebuild(&binary_path, source_path)?;
846 if rebuild_is_needed {
847 let old_bin_path = format!("{binary_path}.old");
848 log!(INFO, "RENAMING: {binary_path} -> {old_bin_path}");
849 Rob::rename(binary_path, &old_bin_path)?;
850
851 let self_compilation_output = Rob::new()
852 .append(&["rustc -o", binary_path, source_path])
853 .execute_sync();
854
855 match self_compilation_output {
856 Ok(out) => if !out.status.success() {
857 let stderr = String::from_utf8_lossy(&out.stderr);
858 log!(ERROR, "{stderr}");
859 let code = out.status.code()
860 .expect("Process terminated by signal");
861 log!(ERROR, "Compilation exited abnormally with code: {code}");
862 Rob::rename(old_bin_path.as_str(), binary_path)?;
863 exit(1);
864 }
865 Err(err) => {
866 log!(ERROR, "Failed to rename file: {old_bin_path}: {err}");
867 Rob::rename(old_bin_path.as_str(), binary_path)?;
868 exit(1);
869 }
870 }
871
872 match Rob::new()
873 .append(&args)
874 .execute_sync()
875 {
876 Ok(_) => {
877 log!(INFO, "Removing: {old_bin_path}");
878 Rob::rm_if_exists(old_bin_path);
879 exit(0);
880 }
881 Err(err) => {
882 log!(ERROR, "Failed to restart rob from file: {binary_path}: {err}");
883 exit(1);
884 }
885 }
886 } Ok(())
887 }
888
889 #[inline]
890 pub fn get_last_modification_time<P>(p: P) -> IoResult::<SystemTime>
891 where
892 P: AsRef::<Path>
893 {
894 metadata(p)?.modified()
895 }
896
897 #[inline]
899 pub fn noext(p: &str) -> String {
900 p.chars().take_while(|x| *x != '.').collect()
901 }
902
903 #[inline]
904 pub fn is_file<P>(p: P) -> bool
905 where
906 P: Into::<PathBuf>
907 {
908 p.into().is_file()
909 }
910
911 #[inline]
912 pub fn is_dir<P>(p: P) -> bool
913 where
914 P: Into::<PathBuf>
915 {
916 p.into().is_dir()
917 }
918
919 #[inline]
920 pub fn path_exists<P>(p: P) -> bool
921 where
922 P: Into::<PathBuf>
923 {
924 p.into().exists()
925 }
926
927 #[inline]
928 pub fn rename<P>(from: P, to: P) -> IoResult<()>
929 where
930 P: AsRef::<Path>
931 {
932 rename(from, to)
933 }
934
935 #[inline]
936 pub fn mkdir<P>(p: P) -> IoResult::<()>
937 where
938 P: Into::<PathBuf>
939 {
940 create_dir_all(p.into())
941 }
942
943 pub fn rm_if_exists<P>(p: P)
944 where
945 P: Into::<PathBuf> + ToOwned::<Owned = String>
946 {
947 if Rob::is_dir(p.to_owned()) {
948 remove_dir_all(p.into()).expect("Failed to remove directory")
949 } else if Rob::is_file(p.to_owned()) {
950 remove_file(p.into()).expect("Failed to remove file")
951 }
952 }
953
954 pub fn rm<P>(p: P) -> IoResult::<()>
955 where
956 P: Into::<PathBuf> + ToOwned::<Owned = String>
957 {
958 if !Rob::path_exists(p.to_owned()) {
959 return Err(ErrorKind::NotFound.into())
960 } else if Rob::is_dir(p.to_owned()) {
961 remove_dir_all(p.into())
962 } else if Rob::is_file(p.to_owned()) {
963 remove_file(p.into())
964 } else {
965 Err(ErrorKind::InvalidData.into())
966 }
967 }
968
969 #[track_caller]
970 pub fn log(lvl: LogLevel, out: &str) {
971 use LogLevel::*;
972 match lvl {
973 PANIC => panic!("{lvl} {out}"),
974 _ => println!("{lvl} {out}")
975 }
976 }
977
978 #[inline]
979 pub fn echo(&mut self, echo: bool) -> &mut Self {
980 self.cfg.echo(echo);
981 self
982 }
983
984 #[inline]
985 pub fn keepgoing(&mut self, keepgoing: bool) -> &mut Self {
986 self.cfg.keepgoing(keepgoing);
987 self
988 }
989
990 pub fn append<S>(&mut self, xs: &[S]) -> &mut Self
991 where
992 S: ToString + PartialEq::<&'static str>
993 {
994 self.cmd.append(xs);
995 self
996 }
997
998 pub fn append_mv<S>(&mut self, xs: &[S]) -> &mut Self
999 where
1000 S: ToString + PartialEq::<&'static str>
1001 {
1002 self.cmd.append(xs);
1003 self.cmd.move_acp_ptr();
1004 self
1005 }
1006
1007 fn execute_jobs(&mut self, sync: bool) -> RobResult::<Vec::<Vec::<Output>>> {
1008 let mut outss = Vec::new();
1009 for job in self.jobs.iter_mut() {
1010 let outs = if sync {
1011 job.execute_sync()
1012 } else {
1013 job.execute_async()
1014 }?;
1015 outss.push(outs);
1016 } Ok(outss)
1017 }
1018
1019 #[inline]
1020 pub fn execute_jobs_sync(&mut self) -> RobResult::<Vec::<Vec::<Output>>> {
1021 self.execute_jobs(true)
1022 }
1023
1024 #[inline]
1025 pub fn execute_jobs_async(&mut self) -> RobResult::<Vec::<Vec::<Output>>> {
1026 self.execute_jobs(false)
1027 }
1028
1029 #[inline]
1030 pub fn append_job_job(&mut self, job: Job) -> &mut Self {
1031 self.jobs.push(job);
1032 self
1033 }
1034
1035 #[inline]
1036 pub fn append_job<S>(&mut self, target: &str, deps: Vec::<S>, cmd: RobCommand) -> &mut Self
1037 where
1038 S: Into::<String>
1039 {
1040 let job = Job::new(target, deps, cmd);
1041 self.jobs.push(job);
1042 self
1043 }
1044
1045 #[inline]
1046 pub fn execute(&mut self) -> IoResult::<&mut Self> {
1047 self.cmd.execute()?;
1048 Ok(self)
1049 }
1050
1051 #[inline]
1052 pub fn execute_sync(&mut self) -> IoResult::<Output> {
1053 self.cmd.execute_sync()
1054 }
1055
1056 #[inline]
1057 pub fn execute_all_sync(&mut self) -> RobResult::<Vec::<Output>> {
1058 self.cmd.execute_all_sync()
1059 }
1060
1061 #[inline]
1062 pub fn output(&mut self) -> Option::<Output> {
1063 self.cmd.output()
1064 }
1065
1066 #[inline]
1067 pub fn outputs_refs(&self) -> VecDeque::<&Output> {
1068 self.cmd.outputs_refs()
1069 }
1070
1071 #[inline]
1072 pub fn outputs(self) -> VecDeque::<Output> {
1073 self.cmd.outputs()
1074 }
1075}
1076
1077