commit 94cdce087f400980a56b857acad7868c50cddc4b Author: LordMZTE Date: Sun Oct 10 20:22:25 2021 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..77b6ab3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "upgr" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "3.0.0-beta.4", features = ["derive"] } +miette = { version = "3.2.0", features = ["fancy"] } +owo-colors = "2.1.0" +serde = { version = "1.0.130", features = ["derive"] } +thiserror = "1.0.30" +toml = "0.5.8" diff --git a/exampleconfig.toml b/exampleconfig.toml new file mode 100644 index 0000000..2852d90 --- /dev/null +++ b/exampleconfig.toml @@ -0,0 +1,4 @@ +[[steps]] +unint_alt = ["paru", "-Syu", "--noconfirm"] +interactive = true +command = ["paru", "-Syu"] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..1059111 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,12 @@ +unstable_features = true +binop_separator = "Back" +format_code_in_doc_comments = true +format_macro_matchers = true +format_strings = true +imports_layout = "HorizontalVertical" +match_block_trailing_comma = true +merge_imports = true +normalize_comments = true +use_field_init_shorthand = true +use_try_shorthand = true +wrap_comments = true diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..2d90108 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,67 @@ +use miette::Diagnostic; +use std::process::Command; +use thiserror::Error; + +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct Config { + pub steps: Vec, + #[serde(default = "default_shell")] + pub shell: Vec, +} + +fn default_shell() -> Vec { + vec!["sh".to_string(), "-c".to_string()] +} + +#[derive(Debug, Deserialize)] +pub struct Step { + pub command: CfgCommand, + #[serde(default)] + pub interactive: bool, + pub unint_alt: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum CfgCommand { + Shell(String), + Args(Vec), +} + +impl ToString for CfgCommand { + fn to_string(&self) -> String { + match self { + Self::Shell(cmd) => cmd.clone(), + Self::Args(args) => args.join(" "), + } + } +} + +#[derive(Debug, Error, Diagnostic)] +#[diagnostic(code)] +pub enum IntoCommandError { + #[error("Command has empty args!")] + EmptyArgs, + + #[error("Shell command is empty!")] + EmptyShell, +} + +impl CfgCommand { + pub fn into_command(self, shell: &[String]) -> Result { + match self { + Self::Args(args) => { + let mut cmd = Command::new(args.first().ok_or(IntoCommandError::EmptyArgs)?); + cmd.args(&args[1..]); + Ok(cmd) + } + Self::Shell(shell_cmd) => { + let mut cmd = Command::new(shell.first().ok_or(IntoCommandError::EmptyShell)?); + cmd.args(&shell[1..]).arg(shell_cmd); + Ok(cmd) + } + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9209ac6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,66 @@ +use clap::Clap; +use config::CfgCommand; +use miette::{Context, IntoDiagnostic}; +use owo_colors::OwoColorize; +use std::fs::File; +use std::{io::Read, path::PathBuf}; + +use crate::config::Config; + +pub mod config; + +#[derive(Clap)] +struct Opt { + #[clap(long, short)] + uninteractive: bool, + + #[clap(long, short, default_value = "~/.config/upgr/config.toml")] + config: PathBuf, +} + +fn main() -> miette::Result<()> { + let opt = Opt::parse(); + + let mut file = File::open(opt.config) + .into_diagnostic() + .wrap_err("Couldn't open config")?; + + let mut config_bytes = vec![]; + file.read_to_end(&mut config_bytes) + .into_diagnostic() + .wrap_err("Failed to read config")?; + + let config = toml::from_slice::(&config_bytes) + .into_diagnostic() + .wrap_err("Failed to parse config")?; + + for step in config.steps { + if step.interactive && opt.uninteractive { + if let Some(cmd) = step.unint_alt { + exec_cmd(cmd, &config.shell)?; + } + } else { + exec_cmd(step.command, &config.shell)?; + } + } + + Ok(()) +} + +fn exec_cmd(cmd: CfgCommand, shell: &[String]) -> miette::Result<()> { + let cmd_str = cmd.to_string(); + let mut cmd = cmd.into_command(shell)?; + let len = 8 + cmd_str.len(); + + println!( + "{:=