Upgrade error handeling
This commit is contained in:
parent
804584d21c
commit
5f6cba1469
@ -7,12 +7,22 @@ use std::process::{Command, ExitStatus, Stdio};
|
|||||||
use std::{fmt, fs, io};
|
use std::{fmt, fs, io};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
enum UpdateSteps {
|
pub enum UpdateSteps {
|
||||||
PreInstall,
|
PreInstall,
|
||||||
Install,
|
Install,
|
||||||
PostInstall,
|
PostInstall,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for UpdateSteps {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
UpdateSteps::PreInstall => write!(f, "PreInstall"),
|
||||||
|
UpdateSteps::Install => write!(f, "Install"),
|
||||||
|
UpdateSteps::PostInstall => write!(f, "PostInstall"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: change the struct’s names for the `topgrade`’s one. They are much better.
|
// TODO: change the struct’s names for the `topgrade`’s one. They are much better.
|
||||||
|
|
||||||
/// Root of the machine’s dependency graph
|
/// Root of the machine’s dependency graph
|
||||||
@ -43,7 +53,7 @@ pub struct Executor {
|
|||||||
|
|
||||||
/// A command to execute on the system as part of an executor
|
/// A command to execute on the system as part of an executor
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
struct Cmd {
|
pub struct Cmd {
|
||||||
exe: String,
|
exe: String,
|
||||||
params: Option<Vec<String>>,
|
params: Option<Vec<String>>,
|
||||||
current_dir: Option<PathBuf>,
|
current_dir: Option<PathBuf>,
|
||||||
@ -52,7 +62,7 @@ struct Cmd {
|
|||||||
|
|
||||||
/// The actual (cleaned) command that will be executed on the system as part of an executor
|
/// The actual (cleaned) command that will be executed on the system as part of an executor
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub(crate) struct ActualCmd {
|
pub struct ActualCmd {
|
||||||
exe: String,
|
exe: String,
|
||||||
params: Vec<String>,
|
params: Vec<String>,
|
||||||
current_dir: Option<PathBuf>,
|
current_dir: Option<PathBuf>,
|
||||||
@ -72,6 +82,10 @@ impl From<&String> for UpdateSteps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_packages_folder(opt: &Opt) -> io::Result<PathBuf> {
|
pub fn get_packages_folder(opt: &Opt) -> io::Result<PathBuf> {
|
||||||
|
if let Some(p) = opt.config_folder.clone() {
|
||||||
|
return Ok(p);
|
||||||
|
}
|
||||||
|
|
||||||
let config_folder = directories::ProjectDirs::from("net", "ZykiCorp", "System Updater")
|
let config_folder = directories::ProjectDirs::from("net", "ZykiCorp", "System Updater")
|
||||||
.ok_or(io::Error::new(
|
.ok_or(io::Error::new(
|
||||||
io::ErrorKind::NotFound,
|
io::ErrorKind::NotFound,
|
||||||
@ -154,6 +168,7 @@ impl Updater {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let packages_folder = get_packages_folder(&opt)?;
|
let packages_folder = get_packages_folder(&opt)?;
|
||||||
|
// TODO: Useless match? Still a risck for "time-of-check to time-of-use" bug
|
||||||
match packages_folder.try_exists() {
|
match packages_folder.try_exists() {
|
||||||
Ok(true) => {} // Ok: Exist and should be readable
|
Ok(true) => {} // Ok: Exist and should be readable
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
@ -178,7 +193,7 @@ impl Updater {
|
|||||||
}
|
}
|
||||||
Err(..) => false,
|
Err(..) => false,
|
||||||
}) {
|
}) {
|
||||||
let file = packages_folder.join(file?.path()); // "default.yaml");
|
let file = file?.path();
|
||||||
let sys = std::fs::read_to_string(&file).unwrap();
|
let sys = std::fs::read_to_string(&file).unwrap();
|
||||||
let sys = serde_yaml::from_str(&sys).map_err(|err| {
|
let sys = serde_yaml::from_str(&sys).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
@ -244,16 +259,18 @@ impl Executor {
|
|||||||
if let Some(pre_install) = &self.pre_install {
|
if let Some(pre_install) = &self.pre_install {
|
||||||
for cmd in pre_install {
|
for cmd in pre_install {
|
||||||
let cmd = cmd.clone().prepare(opt);
|
let cmd = cmd.clone().prepare(opt);
|
||||||
let exit_status = cmd
|
let exit_status = cmd.execute(opt).map_err(|err| Error::Execution {
|
||||||
.execute(opt)
|
source: err,
|
||||||
.map_err(|err| MyError::new(MyErrorKind::PreInstall, err, cmd.clone()))?;
|
step: UpdateSteps::PreInstall,
|
||||||
|
cmd: cmd.clone(),
|
||||||
|
})?;
|
||||||
|
|
||||||
if !exit_status.success() {
|
if !exit_status.success() {
|
||||||
return Err(MyError::new(
|
return Err(Error::Execution {
|
||||||
MyErrorKind::PreInstall,
|
source: io::Error::new(io::ErrorKind::Other, format!("{}", exit_status)),
|
||||||
io::Error::new(io::ErrorKind::Other, format!("{}", exit_status)),
|
step: UpdateSteps::PreInstall,
|
||||||
cmd.clone(),
|
cmd,
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,16 +279,18 @@ impl Executor {
|
|||||||
|
|
||||||
pub fn install(&self, opt: &Opt) -> Result<()> {
|
pub fn install(&self, opt: &Opt) -> Result<()> {
|
||||||
let cmd = self.install.clone().prepare(opt);
|
let cmd = self.install.clone().prepare(opt);
|
||||||
let exit_status = cmd
|
let exit_status = cmd.execute(opt).map_err(|err| Error::Execution {
|
||||||
.execute(opt)
|
source: err,
|
||||||
.map_err(|err| MyError::new(MyErrorKind::Install, err, cmd.clone()))?;
|
step: UpdateSteps::Install,
|
||||||
|
cmd: cmd.clone(),
|
||||||
|
})?;
|
||||||
|
|
||||||
if !exit_status.success() {
|
if !exit_status.success() {
|
||||||
return Err(MyError::new(
|
return Err(Error::Execution {
|
||||||
MyErrorKind::Install,
|
source: io::Error::new(io::ErrorKind::Other, format!("{}", exit_status)),
|
||||||
io::Error::new(io::ErrorKind::Other, format!("{}", exit_status)),
|
step: UpdateSteps::Install,
|
||||||
cmd.clone(),
|
cmd,
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -280,16 +299,18 @@ impl Executor {
|
|||||||
if let Some(post_install) = &self.post_install {
|
if let Some(post_install) = &self.post_install {
|
||||||
for cmd in post_install {
|
for cmd in post_install {
|
||||||
let cmd = cmd.clone().prepare(opt);
|
let cmd = cmd.clone().prepare(opt);
|
||||||
let exit_status = cmd
|
let exit_status = cmd.execute(opt).map_err(|err| Error::Execution {
|
||||||
.execute(opt)
|
source: err,
|
||||||
.map_err(|err| MyError::new(MyErrorKind::PostInstall, err, cmd.clone()))?;
|
step: UpdateSteps::PostInstall,
|
||||||
|
cmd: cmd.clone(),
|
||||||
|
})?;
|
||||||
|
|
||||||
if !exit_status.success() {
|
if !exit_status.success() {
|
||||||
return Err(MyError::new(
|
return Err(Error::Execution {
|
||||||
MyErrorKind::PostInstall,
|
source: io::Error::new(io::ErrorKind::Other, format!("{}", exit_status)),
|
||||||
io::Error::new(io::ErrorKind::Other, format!("{}", exit_status)),
|
step: UpdateSteps::PostInstall,
|
||||||
cmd.clone(),
|
cmd,
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,75 +1,27 @@
|
|||||||
|
// Use https://kazlauskas.me/entries/errors as a reference
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt;
|
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, MyError>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
/// 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)]
|
|
||||||
pub struct MyError {
|
|
||||||
source: io::Error,
|
|
||||||
kind: MyErrorKind,
|
|
||||||
cmd: ActualCmd,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum MyErrorKind {
|
#[derive(Debug, Error)]
|
||||||
Config,
|
pub enum Error {
|
||||||
PreInstall,
|
// #[error("TODO")]
|
||||||
Install,
|
// Config,
|
||||||
PostInstall,
|
#[error(
|
||||||
}
|
"Could not achieve the \"{step}\" step. Command `{cmd}` resulted in an exited with: {source}"
|
||||||
|
)]
|
||||||
impl MyError {
|
Execution {
|
||||||
pub(crate) fn new(kind: MyErrorKind, source: io::Error, cmd: ActualCmd) -> MyError {
|
source: io::Error,
|
||||||
MyError { source, kind, cmd }
|
step: UpdateSteps,
|
||||||
}
|
cmd: ActualCmd,
|
||||||
|
},
|
||||||
/// Return the kind of this error.
|
|
||||||
pub fn kind(&self) -> &MyErrorKind {
|
|
||||||
&self.kind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for MyError {
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
Some(&self.source)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for MyError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self.kind {
|
|
||||||
MyErrorKind::Config => write!(
|
|
||||||
f,
|
|
||||||
"Could not read configuration file: {}",
|
|
||||||
self.source().unwrap()
|
|
||||||
),
|
|
||||||
MyErrorKind::PreInstall => write!(
|
|
||||||
f,
|
|
||||||
"Could not do the pre_install with command {}: {}",
|
|
||||||
self.cmd,
|
|
||||||
self.source().unwrap()
|
|
||||||
),
|
|
||||||
MyErrorKind::Install => write!(
|
|
||||||
f,
|
|
||||||
"Could not install with command {}: {}",
|
|
||||||
self.cmd,
|
|
||||||
self.source().unwrap()
|
|
||||||
),
|
|
||||||
MyErrorKind::PostInstall => write!(
|
|
||||||
f,
|
|
||||||
"Could not do the post_install command {}: {}",
|
|
||||||
self.cmd,
|
|
||||||
self.source().unwrap()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use std::path::PathBuf;
|
|||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub struct Opt {
|
pub struct Opt {
|
||||||
#[arg(hide = true)] // TODO: hidden option for debug? or usefull for everyone?
|
#[arg(hide = true)] // TODO: hidden option for debug? or usefull for everyone?
|
||||||
pub config_file: Option<PathBuf>,
|
pub config_folder: Option<PathBuf>,
|
||||||
|
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub quiet: bool, // TODO: use clap_verbosity_flag instead
|
pub quiet: bool, // TODO: use clap_verbosity_flag instead
|
||||||
@ -24,7 +24,7 @@ pub struct Opt {
|
|||||||
enum Status {
|
enum Status {
|
||||||
/// All steps asked were successful
|
/// All steps asked were successful
|
||||||
Ok,
|
Ok,
|
||||||
Err(MyError),
|
Err(Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Status {
|
impl Display for Status {
|
||||||
|
Loading…
Reference in New Issue
Block a user