Split config files and use them in the default config path
This commit is contained in:
116
src/command.rs
116
src/command.rs
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
||||
use std::io::{stdout, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, ExitStatus, Stdio};
|
||||
use std::{fmt, io};
|
||||
use std::{fmt, fs, io};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
enum UpdateSteps {
|
||||
@ -16,11 +16,17 @@ enum UpdateSteps {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Updater {
|
||||
systems: Vec<System>,
|
||||
pub systems: Vec<System>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct System {
|
||||
packages: Vec<Package>,
|
||||
// TODO: => make a system dependend on another? This will allow to give a "Rust" config which update "rustup", and a custom "git helix" could then be executed after (with the updated toolchain, and NOT concurrently)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Package {
|
||||
pub name: String,
|
||||
fetch: Option<Cmd>,
|
||||
compile: Option<Cmd>,
|
||||
@ -74,54 +80,120 @@ impl From<&String> for UpdateSteps {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_packages_folder(opt: &Opt) -> io::Result<PathBuf> {
|
||||
let config_folder = directories::ProjectDirs::from("net", "ZykiCorp", "System Updater")
|
||||
.ok_or(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"System’s configuration folder: for its standard location see https://docs.rs/directories/latest/directories/struct.ProjectDirs.html#method.config_dir",
|
||||
))?
|
||||
.config_dir()
|
||||
.join("packages");
|
||||
|
||||
Ok(config_folder)
|
||||
}
|
||||
|
||||
impl Updater {
|
||||
fn new() -> Updater {
|
||||
let mut up = Updater { systems: vec![] };
|
||||
up.systems.push(System {
|
||||
name: Default::default(),
|
||||
fetch: None,
|
||||
compile: None,
|
||||
install: Cmd::new(),
|
||||
post_install: None,
|
||||
});
|
||||
up
|
||||
Updater { systems: vec![] }
|
||||
}
|
||||
|
||||
/// To create a sample config from code
|
||||
// /// To create a sample config from code
|
||||
#[doc(hidden)]
|
||||
fn write_config(&self, opt: &Opt) {
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
let config_folder = get_packages_folder(&opt).unwrap();
|
||||
|
||||
let mut f = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&opt.config_file)
|
||||
.open(config_folder.join("default.yaml"))
|
||||
.unwrap();
|
||||
|
||||
fs::create_dir_all(&config_folder).unwrap();
|
||||
|
||||
f.write_all(serde_yaml::to_string(&self).unwrap().as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn from_config(opt: &Opt) -> Result<Updater> {
|
||||
// let u = Updater::new();
|
||||
// u.write_config(opt);
|
||||
// TODO: add option to use &opt.config_file (or folder?) instead
|
||||
pub fn from_config(opt: &Opt) -> io::Result<Updater> {
|
||||
let mut updater = Updater::new();
|
||||
|
||||
let file = std::fs::read_to_string(&opt.config_file).unwrap();
|
||||
Ok(serde_yaml::from_str(&file).unwrap())
|
||||
if false {
|
||||
updater.systems.push(System { packages: vec![] });
|
||||
updater.systems[0].packages.push(Package {
|
||||
name: "apt".to_owned(),
|
||||
fetch: None,
|
||||
compile: None,
|
||||
install: Cmd {
|
||||
exe: "()".to_owned(),
|
||||
params: vec![],
|
||||
current_dir: None,
|
||||
env: HashMap::new(),
|
||||
},
|
||||
post_install: None,
|
||||
});
|
||||
|
||||
updater.systems[0].packages.push(Package {
|
||||
name: "Rustup".to_owned(),
|
||||
fetch: None,
|
||||
compile: None,
|
||||
install: Cmd {
|
||||
exe: "rustup".to_owned(),
|
||||
params: vec!["self".to_owned(), "update".to_owned()],
|
||||
current_dir: None,
|
||||
env: HashMap::new(),
|
||||
},
|
||||
post_install: None,
|
||||
});
|
||||
updater.write_config(opt);
|
||||
|
||||
panic!("Wrote a config sample.");
|
||||
}
|
||||
|
||||
let packages_folder = get_packages_folder(&opt)?;
|
||||
match packages_folder.try_exists() {
|
||||
Ok(true) => {} // Ok: Exist and should be readable
|
||||
Ok(false) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"Configuration folder not accessible at: {}. (broken symlink?)",
|
||||
packages_folder.display()
|
||||
),
|
||||
))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
|
||||
for file in packages_folder.read_dir()? {
|
||||
let file = packages_folder.join(file?.path()); // "default.yaml");
|
||||
let sys = std::fs::read_to_string(&file).unwrap();
|
||||
let toto = serde_yaml::from_str(&sys).unwrap();
|
||||
updater.systems.push(toto);
|
||||
}
|
||||
|
||||
eprintln!("{:#?}", updater);
|
||||
|
||||
Ok(updater)
|
||||
}
|
||||
|
||||
pub fn update_all(&self, opt: &Opt) -> Summary {
|
||||
// self.systems.iter().collect();
|
||||
let mut status: Vec<_> = vec![];
|
||||
|
||||
// XXX: We may parallelise (iter_par from rayon?) this loop. But the UI will be problematic to handle
|
||||
for sys in &self.systems {
|
||||
status.push((sys.name.clone(), self.update(&sys, opt).into()));
|
||||
for pkg in &sys.packages {
|
||||
status.push((pkg.name.clone(), self.update(&pkg, opt).into()));
|
||||
}
|
||||
}
|
||||
|
||||
Summary { status }
|
||||
}
|
||||
|
||||
fn update(&self, sys: &System, opt: &Opt) -> Result<()> {
|
||||
fn update(&self, sys: &Package, opt: &Opt) -> Result<()> {
|
||||
// TODO: compute once before calling this function, maybe?
|
||||
let steps = if opt.steps.is_empty() {
|
||||
vec![
|
||||
@ -151,7 +223,7 @@ impl Updater {
|
||||
}
|
||||
}
|
||||
|
||||
impl System {
|
||||
impl Package {
|
||||
pub fn fetch(&self, opt: &Opt) -> Result<()> {
|
||||
if let Some(fetch) = &self.fetch {
|
||||
let cmd = fetch.clone().prepare(opt);
|
||||
|
32
src/lib.rs
32
src/lib.rs
@ -1,25 +1,18 @@
|
||||
mod command;
|
||||
mod errors;
|
||||
pub mod errors;
|
||||
|
||||
use command::*;
|
||||
use errors::*;
|
||||
|
||||
use clap::Parser;
|
||||
use std::fmt::Display;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Opt {
|
||||
// TODO: transform this to "base" folder?
|
||||
// XXX: use the directory/directory-next crate to get the "~/.config/sup" folder?
|
||||
// ++ A config subFolder and execute in alphabetical order?
|
||||
// + One config file? -> confy crate?
|
||||
// - A master config file that list the sub/real files? no if it mean parsing 2 differents formats
|
||||
//
|
||||
// Default to something like -> "~/.config/system-updater/list.yml".into(),
|
||||
//
|
||||
//#[arg(default_value_t = PathBuf::from("examples/debian.yml"))]
|
||||
pub config_file: PathBuf,
|
||||
#[arg(hide = true)] // TODO: hidden option for debug? or usefull for everyone?
|
||||
pub config_file: Option<PathBuf>,
|
||||
|
||||
#[arg(short, long)]
|
||||
pub quiet: bool, // TODO: use clap_verbosity_flag instead
|
||||
@ -72,8 +65,19 @@ impl Display for Summary {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(opt: &Opt) -> Summary {
|
||||
let updater = Updater::from_config(opt).unwrap();
|
||||
pub fn run(opt: &Opt) -> io::Result<Summary> {
|
||||
let updater = Updater::from_config(opt)?;
|
||||
|
||||
updater.update_all(opt)
|
||||
if updater.systems.is_empty() {
|
||||
let package_folder = get_packages_folder(&opt)?;
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"No package found in configuration folder. Add them in: {}",
|
||||
package_folder.display()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(updater.update_all(opt))
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
use clap::Parser;
|
||||
use system_updater::*;
|
||||
|
||||
fn main() {
|
||||
use clap::Parser;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let opt = Opt::parse();
|
||||
|
||||
let summary = run(&opt);
|
||||
let summary = run(&opt)?;
|
||||
|
||||
println!("{}", summary);
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user