2019-05-21 16:40:00 +03:00
|
|
|
use chrono::{Local, Timelike};
|
2018-11-01 11:28:27 +02:00
|
|
|
use console::{style, Term};
|
2018-12-05 11:34:08 +02:00
|
|
|
use lazy_static::lazy_static;
|
2018-05-31 16:00:01 +03:00
|
|
|
use std::cmp::{max, min};
|
2019-06-04 09:35:29 +03:00
|
|
|
use std::env;
|
2018-10-17 14:07:58 +03:00
|
|
|
use std::io::{self, Write};
|
2019-05-23 08:52:48 +03:00
|
|
|
use std::process::Command;
|
2018-12-05 11:34:08 +02:00
|
|
|
use std::sync::Mutex;
|
2019-07-31 11:53:05 +03:00
|
|
|
#[cfg(windows)]
|
2019-08-14 21:36:57 +03:00
|
|
|
use which_crate::which;
|
2018-05-31 16:00:01 +03:00
|
|
|
|
2018-12-05 11:34:08 +02:00
|
|
|
lazy_static! {
|
|
|
|
|
static ref TERMINAL: Mutex<Terminal> = Mutex::new(Terminal::new());
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-23 08:52:48 +03:00
|
|
|
#[cfg(unix)]
|
2019-08-22 21:46:06 +03:00
|
|
|
pub fn shell() -> String {
|
2019-05-23 08:52:48 +03:00
|
|
|
env::var("SHELL").unwrap_or_else(|_| "sh".to_string())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2019-08-22 21:46:06 +03:00
|
|
|
pub fn shell() -> &'static str {
|
2019-08-14 21:36:57 +03:00
|
|
|
which("pwsh").map(|_| "pwsh").unwrap_or("powershell")
|
2019-05-23 08:52:48 +03:00
|
|
|
}
|
|
|
|
|
|
2019-06-13 22:05:18 +03:00
|
|
|
pub fn run_shell() {
|
|
|
|
|
Command::new(shell()).spawn().unwrap().wait().unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-05 11:34:08 +02:00
|
|
|
struct Terminal {
|
2018-11-01 11:28:27 +02:00
|
|
|
width: Option<u16>,
|
2019-06-04 09:35:29 +03:00
|
|
|
prefix: String,
|
2018-11-01 11:28:27 +02:00
|
|
|
term: Term,
|
2019-09-05 20:52:51 +03:00
|
|
|
set_title: bool,
|
2018-05-31 16:00:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Terminal {
|
2018-12-05 11:34:08 +02:00
|
|
|
fn new() -> Self {
|
2018-11-01 11:28:27 +02:00
|
|
|
let term = Term::stdout();
|
2018-05-31 16:00:01 +03:00
|
|
|
Self {
|
2018-11-01 11:28:27 +02:00
|
|
|
width: term.size_checked().map(|(_, w)| w),
|
|
|
|
|
term,
|
2019-06-04 09:35:29 +03:00
|
|
|
prefix: env::var("TOPGRADE_PREFIX")
|
|
|
|
|
.map(|prefix| format!("({}) ", prefix))
|
|
|
|
|
.unwrap_or_else(|_| String::new()),
|
2019-09-05 20:52:51 +03:00
|
|
|
set_title: true,
|
2018-05-31 16:00:01 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-05 20:52:51 +03:00
|
|
|
fn set_title(&mut self, set_title: bool) {
|
|
|
|
|
self.set_title = set_title
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-05 11:34:08 +02:00
|
|
|
fn print_separator<P: AsRef<str>>(&mut self, message: P) {
|
2019-09-05 20:52:51 +03:00
|
|
|
if self.set_title {
|
|
|
|
|
self.term
|
|
|
|
|
.set_title(format!("{}Topgrade - {}", self.prefix, message.as_ref()));
|
|
|
|
|
}
|
2019-05-21 16:40:00 +03:00
|
|
|
let now = Local::now();
|
|
|
|
|
let message = format!(
|
2019-06-04 09:35:29 +03:00
|
|
|
"{}{:02}:{:02}:{:02} - {}",
|
|
|
|
|
self.prefix,
|
2019-05-21 16:40:00 +03:00
|
|
|
now.hour(),
|
|
|
|
|
now.minute(),
|
|
|
|
|
now.second(),
|
|
|
|
|
message.as_ref()
|
|
|
|
|
);
|
2018-06-04 23:23:25 +03:00
|
|
|
match self.width {
|
|
|
|
|
Some(width) => {
|
2018-12-05 11:34:08 +02:00
|
|
|
self.term
|
|
|
|
|
.write_fmt(format_args!(
|
|
|
|
|
"{}\n",
|
|
|
|
|
style(format_args!(
|
|
|
|
|
"\n―― {} {:―^border$}",
|
|
|
|
|
message,
|
|
|
|
|
"",
|
2019-01-30 10:41:40 +02:00
|
|
|
border = max(
|
|
|
|
|
2,
|
|
|
|
|
min(80, width as usize)
|
2019-03-15 19:22:37 -05:00
|
|
|
.checked_sub(4)
|
2019-01-30 10:41:40 +02:00
|
|
|
.and_then(|e| e.checked_sub(message.len()))
|
|
|
|
|
.unwrap_or(0)
|
|
|
|
|
)
|
2018-12-11 16:00:19 +02:00
|
|
|
))
|
|
|
|
|
.bold()
|
|
|
|
|
))
|
|
|
|
|
.ok();
|
2018-06-04 23:23:25 +03:00
|
|
|
}
|
|
|
|
|
None => {
|
2018-12-05 11:34:08 +02:00
|
|
|
self.term.write_fmt(format_args!("―― {} ――\n", message)).ok();
|
2018-05-31 16:00:01 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-03 16:43:53 +03:00
|
|
|
|
2018-06-27 23:04:39 +03:00
|
|
|
#[allow(dead_code)]
|
2018-12-05 11:34:08 +02:00
|
|
|
fn print_warning<P: AsRef<str>>(&mut self, message: P) {
|
2018-06-04 23:23:25 +03:00
|
|
|
let message = message.as_ref();
|
2018-12-05 11:34:08 +02:00
|
|
|
self.term
|
|
|
|
|
.write_fmt(format_args!("{}\n", style(message).yellow().bold()))
|
|
|
|
|
.ok();
|
2018-06-03 16:43:53 +03:00
|
|
|
}
|
2018-06-03 18:04:58 +03:00
|
|
|
|
2019-06-13 22:05:18 +03:00
|
|
|
#[allow(dead_code)]
|
|
|
|
|
fn print_info<P: AsRef<str>>(&mut self, message: P) {
|
|
|
|
|
let message = message.as_ref();
|
|
|
|
|
self.term
|
2019-06-16 09:09:05 +03:00
|
|
|
.write_fmt(format_args!("{}\n", style(message).blue().bold()))
|
2019-06-13 22:05:18 +03:00
|
|
|
.ok();
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-05 11:34:08 +02:00
|
|
|
fn print_result<P: AsRef<str>>(&mut self, key: P, succeeded: bool) {
|
2018-06-03 18:04:58 +03:00
|
|
|
let key = key.as_ref();
|
|
|
|
|
|
2018-12-05 11:34:08 +02:00
|
|
|
self.term
|
|
|
|
|
.write_fmt(format_args!(
|
|
|
|
|
"{}: {}\n",
|
|
|
|
|
key,
|
|
|
|
|
if succeeded {
|
|
|
|
|
style("OK").bold().green()
|
|
|
|
|
} else {
|
|
|
|
|
style("FAILED").bold().red()
|
|
|
|
|
}
|
2018-12-11 16:00:19 +02:00
|
|
|
))
|
|
|
|
|
.ok();
|
2018-06-03 18:04:58 +03:00
|
|
|
}
|
2018-08-25 22:19:38 +03:00
|
|
|
|
2018-12-31 14:07:55 +02:00
|
|
|
fn should_retry(&mut self, interrupted: bool) -> Result<bool, io::Error> {
|
2018-08-25 22:19:38 +03:00
|
|
|
if self.width.is_none() {
|
2018-10-17 14:07:58 +03:00
|
|
|
return Ok(false);
|
2018-08-25 22:19:38 +03:00
|
|
|
}
|
2018-10-17 14:07:58 +03:00
|
|
|
|
2019-09-05 20:52:51 +03:00
|
|
|
if self.set_title {
|
|
|
|
|
self.term.set_title("Topgrade - Awaiting user");
|
|
|
|
|
}
|
2018-11-10 20:18:43 +02:00
|
|
|
self.term
|
|
|
|
|
.write_fmt(format_args!(
|
2018-12-05 11:34:08 +02:00
|
|
|
"\n{}",
|
2018-11-10 20:18:43 +02:00
|
|
|
style(format!(
|
2019-06-04 09:35:29 +03:00
|
|
|
"{}Retry? (y)es/(N)o/(s)hell {}",
|
|
|
|
|
self.prefix,
|
2018-12-31 14:07:55 +02:00
|
|
|
if interrupted {
|
2018-11-10 20:18:43 +02:00
|
|
|
"(Press Ctrl+C again to stop Topgrade) "
|
|
|
|
|
} else {
|
|
|
|
|
""
|
|
|
|
|
}
|
2018-12-11 16:00:19 +02:00
|
|
|
))
|
|
|
|
|
.yellow()
|
2018-11-10 20:18:43 +02:00
|
|
|
.bold()
|
2018-12-11 16:00:19 +02:00
|
|
|
))
|
|
|
|
|
.ok();
|
2018-08-25 22:19:38 +03:00
|
|
|
|
2018-11-10 20:18:43 +02:00
|
|
|
let answer = loop {
|
|
|
|
|
match self.term.read_char()? {
|
|
|
|
|
'y' | 'Y' => break Ok(true),
|
2019-05-23 08:52:48 +03:00
|
|
|
's' | 'S' => {
|
|
|
|
|
println!("\n\nDropping you to shell. Fix what you need and then exit the shell.\n");
|
2019-06-13 22:05:18 +03:00
|
|
|
run_shell();
|
2019-05-23 08:52:48 +03:00
|
|
|
break Ok(true);
|
|
|
|
|
}
|
2019-05-27 10:35:51 +03:00
|
|
|
'n' | 'N' | '\r' | '\n' => break Ok(false),
|
2018-10-17 14:07:58 +03:00
|
|
|
_ => (),
|
2018-08-25 22:19:38 +03:00
|
|
|
}
|
2018-11-10 20:18:43 +02:00
|
|
|
};
|
|
|
|
|
|
2018-12-05 11:34:08 +02:00
|
|
|
self.term.write_str("\n").ok();
|
|
|
|
|
|
2018-11-10 20:18:43 +02:00
|
|
|
answer
|
2018-08-25 22:19:38 +03:00
|
|
|
}
|
2019-06-13 09:21:39 +03:00
|
|
|
|
2019-06-13 22:05:18 +03:00
|
|
|
fn get_char(&self) -> Result<char, io::Error> {
|
|
|
|
|
self.term.read_char()
|
2019-06-13 09:21:39 +03:00
|
|
|
}
|
2018-05-31 16:00:01 +03:00
|
|
|
}
|
2018-11-12 21:27:49 +02:00
|
|
|
|
|
|
|
|
impl Default for Terminal {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::new()
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-05 11:34:08 +02:00
|
|
|
|
2018-12-31 14:07:55 +02:00
|
|
|
pub fn should_retry(interrupted: bool) -> Result<bool, io::Error> {
|
|
|
|
|
TERMINAL.lock().unwrap().should_retry(interrupted)
|
2018-12-05 11:34:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn print_separator<P: AsRef<str>>(message: P) {
|
|
|
|
|
TERMINAL.lock().unwrap().print_separator(message)
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-21 20:28:23 +02:00
|
|
|
#[allow(dead_code)]
|
2018-12-05 11:34:08 +02:00
|
|
|
pub fn print_warning<P: AsRef<str>>(message: P) {
|
|
|
|
|
TERMINAL.lock().unwrap().print_warning(message)
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-13 22:05:18 +03:00
|
|
|
#[allow(dead_code)]
|
|
|
|
|
pub fn print_info<P: AsRef<str>>(message: P) {
|
|
|
|
|
TERMINAL.lock().unwrap().print_info(message)
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-05 11:34:08 +02:00
|
|
|
pub fn print_result<P: AsRef<str>>(key: P, succeeded: bool) {
|
|
|
|
|
TERMINAL.lock().unwrap().print_result(key, succeeded)
|
|
|
|
|
}
|
2019-01-21 20:12:56 +02:00
|
|
|
|
|
|
|
|
/// Tells whether the terminal is dumb.
|
|
|
|
|
pub fn is_dumb() -> bool {
|
|
|
|
|
TERMINAL.lock().unwrap().width.is_none()
|
|
|
|
|
}
|
2019-06-13 09:21:39 +03:00
|
|
|
|
2019-06-13 22:05:18 +03:00
|
|
|
pub fn get_char() -> char {
|
|
|
|
|
TERMINAL.lock().unwrap().get_char().unwrap()
|
2019-06-13 09:21:39 +03:00
|
|
|
}
|
2019-09-05 20:52:51 +03:00
|
|
|
|
|
|
|
|
pub fn set_title(set_title: bool) {
|
|
|
|
|
TERMINAL.lock().unwrap().set_title(set_title);
|
|
|
|
|
}
|