Print summary infos at the end
This commit is contained in:
parent
088cd97b19
commit
206c8c7bf8
@ -12,7 +12,7 @@ systems:
|
||||
params:
|
||||
- apt
|
||||
- upgrade
|
||||
yes: -y
|
||||
- --yes
|
||||
current_dir: null
|
||||
env: {}
|
||||
- install:
|
||||
@ -28,4 +28,3 @@ systems:
|
||||
- -a
|
||||
current_dir: null
|
||||
env: {}
|
||||
nice: null
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::io::{stdout, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::process::{Command, ExitStatus, Stdio};
|
||||
use std::{fmt, io};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
enum UpdateSteps {
|
||||
@ -32,13 +32,12 @@ pub struct System {
|
||||
struct Cmd {
|
||||
exe: String,
|
||||
params: Vec<String>,
|
||||
yes: Option<String>,
|
||||
current_dir: Option<PathBuf>,
|
||||
env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ActualCmd {
|
||||
pub(crate) struct ActualCmd {
|
||||
exe: String,
|
||||
params: Vec<String>,
|
||||
current_dir: Option<PathBuf>,
|
||||
@ -106,29 +105,13 @@ impl Updater {
|
||||
Ok(serde_yaml::from_str(&file).unwrap())
|
||||
}
|
||||
|
||||
pub fn update_all(&self, opt: &Opt) -> Result<()> {
|
||||
let errors: Vec<_> = self
|
||||
.systems
|
||||
.iter()
|
||||
.filter_map(|sys| {
|
||||
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?
|
||||
))
|
||||
pub fn update_all(&self, opt: &Opt) -> Summary {
|
||||
// self.systems.iter().collect();
|
||||
let mut systems: Vec<_> = vec![];
|
||||
for sys in &self.systems {
|
||||
systems.push(self.update(&sys, opt));
|
||||
}
|
||||
Summary { systems }
|
||||
}
|
||||
|
||||
fn update(&self, sys: &System, opt: &Opt) -> Result<()> {
|
||||
@ -159,20 +142,26 @@ impl Updater {
|
||||
impl System {
|
||||
pub fn fetch(&self, opt: &Opt) -> Result<()> {
|
||||
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(())
|
||||
}
|
||||
|
||||
pub fn compile(&self, opt: &Opt) -> Result<()> {
|
||||
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(())
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@ -182,7 +171,6 @@ impl Cmd {
|
||||
Cmd {
|
||||
exe: "".into(),
|
||||
params: vec![],
|
||||
yes: None,
|
||||
current_dir: None,
|
||||
env: HashMap::new(),
|
||||
}
|
||||
@ -190,11 +178,6 @@ impl Cmd {
|
||||
|
||||
fn prepare(self, opt: &Opt) -> ActualCmd {
|
||||
let mut params = self.params;
|
||||
if opt.yes {
|
||||
if let Some(y) = &self.yes {
|
||||
params.push(y.to_owned());
|
||||
}
|
||||
};
|
||||
|
||||
let mut env = self.env;
|
||||
if !env.contains_key("PATH") {
|
||||
@ -211,45 +194,26 @@ impl Cmd {
|
||||
}
|
||||
|
||||
impl ActualCmd {
|
||||
fn execute(&self, opt: &Opt) -> Result<bool> {
|
||||
fn execute(&self, opt: &Opt) -> io::Result<ExitStatus> {
|
||||
let mut cmd = Command::new(&self.exe);
|
||||
|
||||
cmd.args(&self.params).env_clear().envs(&self.env);
|
||||
|
||||
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!("Will execute {}{}", self, confirm);
|
||||
stdout().flush().unwrap();
|
||||
print!("Executing: {}", self);
|
||||
stdout().flush()?;
|
||||
|
||||
if opt.quiet {
|
||||
// Maybe we should only hide with the quiet option and not with yes?
|
||||
// FIXME: stdin does not work with sudo?
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null());
|
||||
}
|
||||
|
||||
let mut user_continue = String::new();
|
||||
if !opt.yes {
|
||||
std::io::stdin()
|
||||
.read_line(&mut user_continue)
|
||||
.expect("Unable to read user’s 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())
|
||||
Ok(cmd.status()?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,17 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use crate::*;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
use std::io;
|
||||
|
||||
/// An error that can occur in this crate.
|
||||
///
|
||||
/// Generally, this error corresponds to problems with underlying process.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
source: Vec<Error>,
|
||||
pub struct MyError {
|
||||
source: io::Error,
|
||||
kind: ErrorKind,
|
||||
cmd: ActualCmd,
|
||||
}
|
||||
|
||||
#[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?
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn new(source: Vec<Error>, kind: ErrorKind) -> Error {
|
||||
Error { source, kind }
|
||||
impl MyError {
|
||||
pub(crate) fn new(kind: ErrorKind, source: io::Error, cmd: ActualCmd) -> MyError {
|
||||
MyError { source, kind, cmd }
|
||||
}
|
||||
|
||||
/// Return the kind of this error.
|
||||
@ -30,29 +34,38 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
if self.source.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&self.source[0])
|
||||
}
|
||||
impl Error for MyError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(&self.source)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
|
||||
impl Display for MyError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
match self.kind {
|
||||
ErrorKind::Config => write!(
|
||||
f,
|
||||
"Could not read configuration file: {}",
|
||||
self.source().unwrap()
|
||||
),
|
||||
ErrorKind::Fetch => write!(f, "Could not install: {}", self.source().unwrap()),
|
||||
ErrorKind::Compile => write!(f, "Could not install: {}", self.source().unwrap()),
|
||||
ErrorKind::Install => write!(f, "Could not install: {}", self.source().unwrap()),
|
||||
ErrorKind::Fetch => write!(
|
||||
f,
|
||||
"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()
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
46
src/lib.rs
46
src/lib.rs
@ -5,10 +5,11 @@ use command::*;
|
||||
use errors::*;
|
||||
|
||||
use clap::Parser;
|
||||
use std::fmt::Display;
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
pub type Result<T> = result::Result<T, MyError>;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Opt {
|
||||
@ -23,9 +24,6 @@ pub struct Opt {
|
||||
//#[arg(default_value_t = PathBuf::from("examples/debian.yml"))]
|
||||
pub config_file: PathBuf,
|
||||
|
||||
#[arg(short, long)]
|
||||
pub yes: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
pub quiet: bool, // TODO: use clap_verbosity_flag instead
|
||||
|
||||
@ -33,8 +31,44 @@ pub struct Opt {
|
||||
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();
|
||||
|
||||
updater.update_all(opt).unwrap();
|
||||
updater.update_all(opt)
|
||||
}
|
||||
|
@ -3,9 +3,8 @@ use system_updater::*;
|
||||
|
||||
fn main() {
|
||||
let mut opt = Opt::parse();
|
||||
if opt.quiet {
|
||||
opt.yes = true;
|
||||
}
|
||||
|
||||
run(&opt);
|
||||
let summary = run(&opt);
|
||||
|
||||
println!("{}", summary);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user