Initial POC
This commit is contained in:
208
src/command.rs
Normal file
208
src/command.rs
Normal file
@ -0,0 +1,208 @@
|
||||
use crate::*;
|
||||
use enumset::EnumSetType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, EnumSetType)]
|
||||
//#[enumset(serialize_as_list)] // TODO: use it or not?
|
||||
enum UpdateSteps {
|
||||
Fetch,
|
||||
Compile,
|
||||
Install,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Updater {
|
||||
systems: Vec<System>,
|
||||
steps: UpdateSteps,
|
||||
nice: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct System {
|
||||
fetch: Option<Fetch>,
|
||||
compile: Option<Compile>,
|
||||
install: Install,
|
||||
// deps or rDeps : Tree
|
||||
// exclusive_with : List
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Fetch {
|
||||
command: Cmd,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Compile {
|
||||
command: Cmd,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Install {
|
||||
command: Cmd,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Cmd {
|
||||
exe: String,
|
||||
params: Vec<String>,
|
||||
current_dir: Option<PathBuf>,
|
||||
env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Updater {
|
||||
fn new() -> Updater {
|
||||
let mut up = Updater {
|
||||
systems: vec![],
|
||||
steps: UpdateSteps::Fetch,
|
||||
nice: None,
|
||||
};
|
||||
up.systems.push(System {
|
||||
fetch: None,
|
||||
compile: None,
|
||||
install: Install {
|
||||
command: Cmd::new(),
|
||||
},
|
||||
});
|
||||
up
|
||||
}
|
||||
|
||||
/// To create a sample config from code
|
||||
#[doc(hidden)]
|
||||
fn write_config(&self, opt: &Opt) {
|
||||
use ::std::io::Write;
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
let mut f = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&opt.config_file)
|
||||
.unwrap();
|
||||
|
||||
f.write_all(serde_yaml::to_string(&self).unwrap().as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn parse_config(opt: &Opt) -> Result<Updater> {
|
||||
// let u = Updater::new();
|
||||
// u.write_config(opt);
|
||||
|
||||
let file = std::fs::read_to_string(&opt.config_file).unwrap();
|
||||
Ok(serde_yaml::from_str(&file).unwrap())
|
||||
// TODO:
|
||||
}
|
||||
|
||||
pub fn update_all(&self) -> Result<()> {
|
||||
let mut errors = vec![];
|
||||
for sys in &self.systems {
|
||||
if let Err(err) = self.update(sys) {
|
||||
eprintln!("Error catched {}", err);
|
||||
errors.push(err);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
if errors.len() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(
|
||||
errors,
|
||||
ErrorKind::Fetch, // TODO: Why should I choose here?
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&self, sys: &System) -> Result<()> {
|
||||
if self.steps == UpdateSteps::Fetch {
|
||||
sys.fetch()?;
|
||||
}
|
||||
if self.steps == UpdateSteps::Compile {
|
||||
sys.compile()?;
|
||||
}
|
||||
if self.steps == UpdateSteps::Install {
|
||||
sys.install()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub fn fetch(&self) -> Result<()> {
|
||||
if let Some(fetch) = &self.fetch {
|
||||
fetch.command.execute()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compile(&self) -> Result<()> {
|
||||
if let Some(compile) = &self.compile {
|
||||
compile.command.execute()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install(&self) -> Result<()> {
|
||||
self.install.command.execute()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
fn new() -> Cmd {
|
||||
Cmd {
|
||||
exe: "".into(),
|
||||
params: vec![],
|
||||
current_dir: None,
|
||||
env: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(&self) -> Result<()> {
|
||||
let mut cmd = Command::new(&self.exe);
|
||||
cmd.args(&self.params)
|
||||
.env_clear()
|
||||
.envs(&self.env)
|
||||
// .stdout(cfg)
|
||||
// .stderr(cfg)
|
||||
;
|
||||
|
||||
if let Some(cdir) = &self.current_dir {
|
||||
cmd.current_dir(std::fs::canonicalize(cdir).unwrap());
|
||||
}
|
||||
|
||||
println!("Executing {}", self);
|
||||
// TODO: Ask if ok or
|
||||
if cmd.status().unwrap().success() {
|
||||
println!("Youpi !");
|
||||
// Other checks?
|
||||
}
|
||||
println!("Exécutée");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Cmd {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let command = if self.params.is_empty() {
|
||||
self.exe.clone()
|
||||
} else {
|
||||
format!("{} {}", &self.exe, &self.params.join(" "))
|
||||
};
|
||||
write!(f, "`{}`", command)?;
|
||||
if let Some(cdir) = &self.current_dir {
|
||||
write!(f, " in {:?}", cdir)?;
|
||||
}
|
||||
if !self.env.is_empty() {
|
||||
writeln!(f, " with the following environment variable:")?;
|
||||
writeln!(f, "{:?}", self.env)
|
||||
} else {
|
||||
writeln!(f, " without any environment variable.")
|
||||
}
|
||||
}
|
||||
}
|
58
src/errors.rs
Normal file
58
src/errors.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
/// 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>,
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum ErrorKind {
|
||||
Config,
|
||||
Fetch, // TODO: merge into "Update" or "Command" type of error? => Have this as an other level of error?
|
||||
Compile, // 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 {
|
||||
pub(crate) fn new(source: Vec<Error>, kind: ErrorKind) -> Error {
|
||||
Error { source, kind }
|
||||
}
|
||||
|
||||
/// Return the kind of this error.
|
||||
pub fn kind(&self) -> &ErrorKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
20
src/lib.rs
Normal file
20
src/lib.rs
Normal file
@ -0,0 +1,20 @@
|
||||
mod command;
|
||||
mod errors;
|
||||
|
||||
use command::*;
|
||||
use errors::*;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
pub struct Opt {
|
||||
pub config_file: PathBuf,
|
||||
}
|
||||
|
||||
pub fn run(opt: &Opt) {
|
||||
let updater = Updater::parse_config(opt).unwrap();
|
||||
|
||||
dbg!(updater).update_all().unwrap();
|
||||
}
|
13
src/main.rs
Normal file
13
src/main.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use system_updater::*;
|
||||
|
||||
fn main() {
|
||||
let opt = Opt {
|
||||
// + One config file?
|
||||
// + A config subFolder and execute in alphabetical order?
|
||||
// - A master config file that list the sub/real files? no if it mean parsing 2 differents formats
|
||||
//
|
||||
// Hardcoded for now
|
||||
config_file: "examples/openSUSE.yml".into(), // Default to something like -> "~/.config/system-updater/list.yml".into(),
|
||||
};
|
||||
run(&opt);
|
||||
}
|
Reference in New Issue
Block a user