iota_aws_orchestrator/
display.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    fmt::Display,
7    io::{self, Write, stdout},
8};
9
10use crossterm::{
11    cursor::{RestorePosition, SavePosition},
12    style::{Print, PrintStyledContent, Stylize},
13    terminal::{Clear, ClearType},
14};
15use prettytable::format::{self};
16
17use crate::logger;
18
19pub fn header<S: Display>(message: S) {
20    let msg = format!("\n{message}\n");
21    logger::log(&msg);
22    crossterm::execute!(stdout(), PrintStyledContent(msg.green().bold()),).unwrap();
23}
24
25pub fn error<S: Display>(message: S) {
26    let msg = format!("\n{message}\n");
27    logger::log(&msg);
28    crossterm::execute!(stdout(), PrintStyledContent(msg.red().bold()),).unwrap();
29}
30
31pub fn warn<S: Display>(message: S) {
32    let msg = format!("\n{message}\n");
33    logger::log(&msg);
34    crossterm::execute!(stdout(), PrintStyledContent(msg.bold()),).unwrap();
35}
36
37pub fn config<N: Display, V: Display>(name: N, value: V) {
38    logger::log(&format!("{name}: {value}\n"));
39    crossterm::execute!(
40        stdout(),
41        PrintStyledContent(format!("{name}: ").bold()),
42        Print(format!("{value}\n"))
43    )
44    .unwrap();
45}
46
47pub fn confirm<S: Display>(message: S) -> bool {
48    // Print the prompt
49    crossterm::execute!(
50        stdout(),
51        PrintStyledContent(format!("{message} ").bold()),
52        Print("[y/N]: ")
53    )
54    .unwrap();
55
56    // Make sure prompt is visible before waiting for input
57    stdout().flush().unwrap();
58
59    // Read input
60    let mut input = String::new();
61    match io::stdin().read_line(&mut input) {
62        Ok(_) => {
63            let normalized = input.trim().to_lowercase();
64            matches!(normalized.as_str(), "y" | "yes")
65        }
66        Err(_) => false,
67    }
68}
69
70pub fn action<S: Display>(message: S) {
71    let msg: String = format!("{message} ... ");
72    logger::log(&msg);
73    crossterm::execute!(stdout(), Print(&msg), SavePosition).unwrap();
74}
75
76pub fn status<S: Display>(status: S) {
77    let msg = format!("[{status}]");
78    logger::log(&msg);
79    crossterm::execute!(
80        stdout(),
81        RestorePosition,
82        SavePosition,
83        Clear(ClearType::UntilNewLine),
84        Print(&msg)
85    )
86    .unwrap();
87}
88
89pub fn done() {
90    logger::log("[Ok]\n");
91    crossterm::execute!(
92        stdout(),
93        RestorePosition,
94        Clear(ClearType::UntilNewLine),
95        Print(format!("[{}]\n", "Ok".green()))
96    )
97    .unwrap();
98}
99
100pub fn newline() {
101    logger::log("\n");
102    crossterm::execute!(stdout(), Print("\n")).unwrap();
103}
104
105/// Default style for tables printed to stdout.
106pub fn default_table_format() -> format::TableFormat {
107    format::FormatBuilder::new()
108        .separators(
109            &[
110                format::LinePosition::Top,
111                format::LinePosition::Bottom,
112                format::LinePosition::Title,
113            ],
114            format::LineSeparator::new('-', '-', '-', '-'),
115        )
116        .padding(1, 1)
117        .build()
118}
119
120#[cfg(test)]
121mod test {
122    use std::time::Duration;
123
124    use tokio::time::sleep;
125
126    use super::{action, config, done, error, header, newline, warn};
127    use crate::display::status;
128
129    #[tokio::test]
130    #[ignore = "only used to manually check if prints work correctly"]
131    async fn display() {
132        header("This is a header");
133        config("This is a config", 2);
134        action("Running a long function");
135        for i in 0..5 {
136            sleep(Duration::from_secs(1)).await;
137            if i == 2 {
138                warn("This is a warning!");
139            }
140            status(format!("{}/5", i + 1));
141        }
142        done();
143        error("This is an error!");
144        warn("This is a warning!");
145        newline();
146    }
147}