init
This commit is contained in:
commit
94cdce087f
6 changed files with 165 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -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"
|
4
exampleconfig.toml
Normal file
4
exampleconfig.toml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[[steps]]
|
||||||
|
unint_alt = ["paru", "-Syu", "--noconfirm"]
|
||||||
|
interactive = true
|
||||||
|
command = ["paru", "-Syu"]
|
12
rustfmt.toml
Normal file
12
rustfmt.toml
Normal file
|
@ -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
|
67
src/config.rs
Normal file
67
src/config.rs
Normal file
|
@ -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<Step>,
|
||||||
|
#[serde(default = "default_shell")]
|
||||||
|
pub shell: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_shell() -> Vec<String> {
|
||||||
|
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<CfgCommand>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum CfgCommand {
|
||||||
|
Shell(String),
|
||||||
|
Args(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Command, IntoCommandError> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
src/main.rs
Normal file
66
src/main.rs
Normal file
|
@ -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>(&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!(
|
||||||
|
"{:=<len$}\nRunning {}\n{:=<len$}",
|
||||||
|
"",
|
||||||
|
cmd_str.green(),
|
||||||
|
"",
|
||||||
|
len = len
|
||||||
|
);
|
||||||
|
|
||||||
|
cmd.spawn().into_diagnostic()?.wait().into_diagnostic()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in a new issue