polyhorn_cli/core/
mod.rs

1//! Types and functions that are shared between platform-specific
2//! implementations of Polyhorn CLI commands.
3
4use ansi_term::Colour::{Cyan, Fixed, Green};
5use indicatif::{ProgressBar, ProgressStyle};
6
7mod cargo_build;
8mod cargo_rustc;
9mod change_crate_type;
10mod rasterize;
11pub mod tasks;
12
13pub use cargo_build::CargoBuild;
14pub use cargo_rustc::CargoRustc;
15pub use change_crate_type::change_crate_type;
16pub use rasterize::rasterize;
17
18/// Represents an individual task that a CLI command is composed of.
19pub trait Task {
20    /// The type of context that is passed to this task, processed and
21    /// subsequently returned by this task.
22    type Context;
23
24    /// The type of error that this task can return.
25    type Error;
26
27    /// The verb that describes this task (e.g. "Launching" or "Building") that
28    /// is shown to the user while the task is running.
29    fn verb(&self) -> &str;
30
31    /// The message that is shown to the user alongside the verb. This usually
32    /// starts with a lowercase letter (e.g. "[Generating] source tree").
33    fn message(&self) -> &str;
34
35    /// Optional additional text that is shown to the user alongside the
36    /// message. This usually starts with a lowercase letter too (e.g.
37    // "[Generating] [source tree] for Android").
38    fn detail(&self) -> &str;
39
40    /// This function should execute the task.
41    fn run(
42        &self,
43        context: Self::Context,
44        manager: &mut Manager,
45    ) -> Result<Self::Context, Self::Error>;
46}
47
48/// Manager that can provide additional utilities (e.g. progress tracking) to
49/// tasks.
50pub struct Manager<'a> {
51    verb: &'a str,
52    message: &'a str,
53    detail: &'a str,
54}
55
56impl<'a> Manager<'a> {
57    /// Creates a new manager for the given task.
58    pub fn new<T>(task: &'a T) -> Manager<'a>
59    where
60        T: Task + ?Sized,
61    {
62        eprint!(
63            "{} {} {}",
64            Cyan.bold().paint(format!("{:>12}", task.verb())),
65            task.message(),
66            Fixed(8).paint(task.detail())
67        );
68
69        Manager {
70            verb: task.verb(),
71            message: task.message(),
72            detail: task.detail(),
73        }
74    }
75
76    /// Returns a progress bar for the task that corresponds to this manager.
77    pub fn progress_bar(&mut self, len: usize) -> ProgressBar {
78        let bar = ProgressBar::new(len as u64);
79
80        eprint!("\r");
81
82        bar.set_style(
83            ProgressStyle::default_bar()
84                .template(&format!(
85                    "{} [{{bar:57}}] {{pos:>{}}}/{{len}}: {} {}",
86                    Cyan.bold().paint(format!("{:>12}", self.verb)),
87                    1000.0f32.log10().ceil() as usize,
88                    self.message,
89                    Fixed(8).paint(self.detail)
90                ))
91                .progress_chars("=> "),
92        );
93
94        bar
95    }
96}
97
98impl<'a> Drop for Manager<'a> {
99    fn drop(&mut self) {
100        eprintln!(
101            "\r{} {} {} {}",
102            Green.bold().paint(format!("    Finished")),
103            self.verb,
104            self.message,
105            Fixed(8).paint(self.detail)
106        );
107    }
108}
109
110/// Executioner that manes the execution of a sequence of a tasks.
111pub struct Executioner;
112
113impl Executioner {
114    /// Executes the given sequence of tasks with the given initial context. The
115    /// first task receives the initial context. Each subsequent task receives
116    /// the input from the previous task. This function will return the
117    /// resulting context of the last task.
118    pub fn execute<T>(tasks: &[T], context: T::Context) -> Result<T::Context, T::Error>
119    where
120        T: Task,
121    {
122        let mut context = context;
123
124        for task in tasks {
125            let mut manager = Manager::new(task);
126            context = task.run(context, &mut manager)?;
127        }
128
129        Ok(context)
130    }
131}