Print summary infos at the end

This commit is contained in:
Zykino 2023-01-28 19:20:37 +01:00
parent 088cd97b19
commit 206c8c7bf8
5 changed files with 101 additions and 92 deletions

View File

@ -12,7 +12,7 @@ systems:
params: params:
- apt - apt
- upgrade - upgrade
yes: -y - --yes
current_dir: null current_dir: null
env: {} env: {}
- install: - install:
@ -28,4 +28,3 @@ systems:
- -a - -a
current_dir: null current_dir: null
env: {} env: {}
nice: null

View File

@ -1,10 +1,10 @@
use crate::*; use crate::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use std::io::{stdout, Write}; use std::io::{stdout, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::process::{Command, Stdio}; use std::process::{Command, ExitStatus, Stdio};
use std::{fmt, io};
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
enum UpdateSteps { enum UpdateSteps {
@ -32,13 +32,12 @@ pub struct System {
struct Cmd { struct Cmd {
exe: String, exe: String,
params: Vec<String>, params: Vec<String>,
yes: Option<String>,
current_dir: Option<PathBuf>, current_dir: Option<PathBuf>,
env: HashMap<String, String>, env: HashMap<String, String>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct ActualCmd { pub(crate) struct ActualCmd {
exe: String, exe: String,
params: Vec<String>, params: Vec<String>,
current_dir: Option<PathBuf>, current_dir: Option<PathBuf>,
@ -106,29 +105,13 @@ impl Updater {
Ok(serde_yaml::from_str(&file).unwrap()) Ok(serde_yaml::from_str(&file).unwrap())
} }
pub fn update_all(&self, opt: &Opt) -> Result<()> { pub fn update_all(&self, opt: &Opt) -> Summary {
let errors: Vec<_> = self // self.systems.iter().collect();
.systems let mut systems: Vec<_> = vec![];
.iter() for sys in &self.systems {
.filter_map(|sys| { systems.push(self.update(&sys, opt));
if let Err(err) = self.update(sys, opt) {
eprintln!("Error catched {}", err);
Some(err)
} else {
None
}
})
.collect();
// TODO:
if errors.is_empty() {
Ok(())
} else {
Err(Error::new(
errors,
ErrorKind::Fetch, // TODO: Why should I choose here?
))
} }
Summary { systems }
} }
fn update(&self, sys: &System, opt: &Opt) -> Result<()> { fn update(&self, sys: &System, opt: &Opt) -> Result<()> {
@ -159,20 +142,26 @@ impl Updater {
impl System { impl System {
pub fn fetch(&self, opt: &Opt) -> Result<()> { pub fn fetch(&self, opt: &Opt) -> Result<()> {
if let Some(fetch) = &self.fetch { if let Some(fetch) = &self.fetch {
fetch.clone().prepare(opt).execute(opt)?; let cmd = fetch.clone().prepare(opt);
cmd.execute(opt)
.map_err(|err| MyError::new(ErrorKind::Fetch, err, cmd))?;
} }
Ok(()) Ok(())
} }
pub fn compile(&self, opt: &Opt) -> Result<()> { pub fn compile(&self, opt: &Opt) -> Result<()> {
if let Some(compile) = &self.compile { if let Some(compile) = &self.compile {
compile.clone().prepare(opt).execute(opt)?; let cmd = compile.clone().prepare(opt);
cmd.execute(opt)
.map_err(|err| MyError::new(ErrorKind::Compile, err, cmd))?;
} }
Ok(()) Ok(())
} }
pub fn install(&self, opt: &Opt) -> Result<()> { pub fn install(&self, opt: &Opt) -> Result<()> {
self.install.clone().prepare(opt).execute(opt)?; let cmd = self.install.clone().prepare(opt);
cmd.execute(opt)
.map_err(|err| MyError::new(ErrorKind::Install, err, cmd))?;
Ok(()) Ok(())
} }
} }
@ -182,7 +171,6 @@ impl Cmd {
Cmd { Cmd {
exe: "".into(), exe: "".into(),
params: vec![], params: vec![],
yes: None,
current_dir: None, current_dir: None,
env: HashMap::new(), env: HashMap::new(),
} }
@ -190,11 +178,6 @@ impl Cmd {
fn prepare(self, opt: &Opt) -> ActualCmd { fn prepare(self, opt: &Opt) -> ActualCmd {
let mut params = self.params; let mut params = self.params;
if opt.yes {
if let Some(y) = &self.yes {
params.push(y.to_owned());
}
};
let mut env = self.env; let mut env = self.env;
if !env.contains_key("PATH") { if !env.contains_key("PATH") {
@ -211,45 +194,26 @@ impl Cmd {
} }
impl ActualCmd { impl ActualCmd {
fn execute(&self, opt: &Opt) -> Result<bool> { fn execute(&self, opt: &Opt) -> io::Result<ExitStatus> {
let mut cmd = Command::new(&self.exe); let mut cmd = Command::new(&self.exe);
cmd.args(&self.params).env_clear().envs(&self.env); cmd.args(&self.params).env_clear().envs(&self.env);
if let Some(cdir) = &self.current_dir { if let Some(cdir) = &self.current_dir {
cmd.current_dir(std::fs::canonicalize(cdir).unwrap()); cmd.current_dir(std::fs::canonicalize(cdir)?);
} }
let confirm = if opt.yes { "\n" } else { "[Y/n]? " }; print!("Executing: {}", self);
stdout().flush()?;
print!("Will execute {}{}", self, confirm);
stdout().flush().unwrap();
if opt.quiet { if opt.quiet {
// Maybe we should only hide with the quiet option and not with yes?
// FIXME: stdin does not work with sudo? // FIXME: stdin does not work with sudo?
cmd.stdin(Stdio::null()) cmd.stdin(Stdio::null())
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()); .stderr(Stdio::null());
} }
let mut user_continue = String::new(); Ok(cmd.status()?)
if !opt.yes {
std::io::stdin()
.read_line(&mut user_continue)
.expect("Unable to read users input");
}
if user_continue.to_lowercase().starts_with('n') {
eprintln!(
"user_continue? {} {}",
user_continue,
user_continue.to_lowercase().starts_with('n')
);
return Ok(false);
}
Ok(cmd.status().unwrap().success())
} }
} }

View File

@ -1,13 +1,17 @@
use std::error; use crate::*;
use std::fmt;
use std::error::Error;
use std::fmt::{Display, Formatter, Result};
use std::io;
/// An error that can occur in this crate. /// An error that can occur in this crate.
/// ///
/// Generally, this error corresponds to problems with underlying process. /// Generally, this error corresponds to problems with underlying process.
#[derive(Debug)] #[derive(Debug)]
pub struct Error { pub struct MyError {
source: Vec<Error>, source: io::Error,
kind: ErrorKind, kind: ErrorKind,
cmd: ActualCmd,
} }
#[derive(Debug)] #[derive(Debug)]
@ -19,9 +23,9 @@ pub enum ErrorKind {
Install, // TODO: merge into "Update" or "Command" type of error? => Have this as an other level of error? Install, // TODO: merge into "Update" or "Command" type of error? => Have this as an other level of error?
} }
impl Error { impl MyError {
pub(crate) fn new(source: Vec<Error>, kind: ErrorKind) -> Error { pub(crate) fn new(kind: ErrorKind, source: io::Error, cmd: ActualCmd) -> MyError {
Error { source, kind } MyError { source, kind, cmd }
} }
/// Return the kind of this error. /// Return the kind of this error.
@ -30,29 +34,38 @@ impl Error {
} }
} }
impl error::Error for Error { impl Error for MyError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> { fn source(&self) -> Option<&(dyn Error + 'static)> {
if self.source.is_empty() { Some(&self.source)
None
} else {
Some(&self.source[0])
}
} }
} }
impl fmt::Display for Error { impl Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> Result {
use std::error::Error;
match self.kind { match self.kind {
ErrorKind::Config => write!( ErrorKind::Config => write!(
f, f,
"Could not read configuration file: {}", "Could not read configuration file: {}",
self.source().unwrap() self.source().unwrap()
), ),
ErrorKind::Fetch => write!(f, "Could not install: {}", self.source().unwrap()), ErrorKind::Fetch => write!(
ErrorKind::Compile => write!(f, "Could not install: {}", self.source().unwrap()), f,
ErrorKind::Install => write!(f, "Could not install: {}", self.source().unwrap()), "Could not fetch with command `{}`: {}",
self.cmd,
self.source().unwrap()
),
ErrorKind::Compile => write!(
f,
"Could not compile with command `{}`: {}",
self.cmd,
self.source().unwrap()
),
ErrorKind::Install => write!(
f,
"Could not install with command `{}`: {}",
self.cmd,
self.source().unwrap()
),
} }
} }
} }

View File

@ -5,10 +5,11 @@ use command::*;
use errors::*; use errors::*;
use clap::Parser; use clap::Parser;
use std::fmt::Display;
use std::path::PathBuf; use std::path::PathBuf;
use std::result; use std::result;
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, MyError>;
#[derive(Parser)] #[derive(Parser)]
pub struct Opt { pub struct Opt {
@ -23,9 +24,6 @@ pub struct Opt {
//#[arg(default_value_t = PathBuf::from("examples/debian.yml"))] //#[arg(default_value_t = PathBuf::from("examples/debian.yml"))]
pub config_file: PathBuf, pub config_file: PathBuf,
#[arg(short, long)]
pub yes: bool,
#[arg(short, long)] #[arg(short, long)]
pub quiet: bool, // TODO: use clap_verbosity_flag instead pub quiet: bool, // TODO: use clap_verbosity_flag instead
@ -33,8 +31,44 @@ pub struct Opt {
pub steps: Vec<String>, pub steps: Vec<String>,
} }
pub fn run(opt: &Opt) { // enum State {
// /// All steps asked were successful
// Ok,
// Err(MyError),
// }
// impl Display for State {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// match self {
// State::Ok => write!(f, "Ok"),
// State::Err(e) => write!(f, "Error: {}", e),
// // State::Fetch(e) => write!(f, "Fetch error: {}", e),
// // State::Compile(e) => write!(f, "Compile error: {}", e),
// // State::Install(e) => write!(f, "Install error: {}", e),
// }
// }
// }
pub struct Summary {
// TODO: Go back to vectors to keep order?
systems: Vec<Result<()>>,
}
impl Display for Summary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Summary:")?;
for system in &self.systems {
// TODO: also print version before/after, update time
// writeln!(f, "\t{}\t{}", system.0, system.1)?;
writeln!(f, "\t{:?}", system)?;
}
Ok(())
}
}
pub fn run(opt: &Opt) -> Summary {
let updater = Updater::from_config(opt).unwrap(); let updater = Updater::from_config(opt).unwrap();
updater.update_all(opt).unwrap(); updater.update_all(opt)
} }

View File

@ -3,9 +3,8 @@ use system_updater::*;
fn main() { fn main() {
let mut opt = Opt::parse(); let mut opt = Opt::parse();
if opt.quiet {
opt.yes = true;
}
run(&opt); let summary = run(&opt);
println!("{}", summary);
} }