Print summary infos at the end
This commit is contained in:
parent
088cd97b19
commit
206c8c7bf8
@ -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
|
|
||||||
|
@ -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 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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
46
src/lib.rs
46
src/lib.rs
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user