Little Rust tool that generates scripts for run-parts from a central config file
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.8 KiB

#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate fern;
extern crate chrono;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;
use std::fs::File;
use std::fs;
use std::io::prelude::*;
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
#[derive(Serialize, Deserialize)]
pub struct Config {
pub folders: Vec<Folder>,
pub reduce_to_config: bool,
}
#[derive(Serialize, Deserialize)]
pub struct Folder {
pub path: String,
pub scripts: Vec<Script>,
}
#[derive(Serialize, Deserialize)]
pub struct Script {
pub name: String,
pub commands: Vec<String>,
}
fn main() {
let clap = setup_clap();
let mut verbosity : usize = 0;
setup_fern(match clap.occurrences_of("v") {
0 => log::LogLevelFilter::Error,
1 => log::LogLevelFilter::Warn,
2 => log::LogLevelFilter::Info,
3 => log::LogLevelFilter::Debug,
4 => log::LogLevelFilter::Trace,
n => {
verbosity = n as usize;
log::LogLevelFilter::Trace
}
});
check_verbosity(verbosity);
let mut file = match File::open("foo.txt") {
Ok(file) => file,
Err(_) => {
error!("error opening file");
return;
}
};
let mut contents = String::new();
match file.read_to_string(&mut contents) {
Ok(_) => info!("read file to string successfully"),
Err(_) => {
error!("couldn't read file to string");
return;
}
};
let config: Config = match toml::from_str(&contents) {
Ok(config) => {
info!("successfully deserialized config file");
config
}
Err(_) => {
error!("couldn't deserialize config file");
return;
}
};
for folder in config.folders {
if config.reduce_to_config {
match fs::remove_dir_all(&folder.path) {
Ok(_) => info!("cleared folder {}", folder.path),
Err(_) => warn!("couldn't clear folder {}", folder.path),
};
}
match fs::DirBuilder::new().recursive(true).create(&folder.path) {
Err(_) => warn!("couldn't make sure folder {} existed", folder.path),
_ => {}
}
for script in folder.scripts {
let mut script_string = String::new();
script_string.push_str("#!/bin/sh\n");
script_string.push_str("# DO NOT EDIT THIS MANUALLY\n");
script_string.push_str("# THIS HAS BEEN GENERATED BY run-parts-gen-rs\n");
for command in script.commands {
script_string.push_str(&command);
script_string.push_str("\n");
}
let mut script_file = match File::create(&format!(
"{}{}{}",
folder.path,
std::path::MAIN_SEPARATOR,
script.name
)) {
Ok(file) => file,
Err(_) => {
warn!(
"couldn't open script {}{}{} for writing",
folder.path,
std::path::MAIN_SEPARATOR,
script.name
);
continue;
}
};
match script_file.write_all(script_string.as_bytes()) {
Ok(_) => info!("script {} written successfully", script.name),
Err(_) => warn!("error writing script {}", script.name),
};
match script_file.set_permissions(Permissions::from_mode(0o755)) {
Ok(_) => info!("made script {} executable", script.name),
Err(_) => warn!("couldn't make script {} executable", script.name),
}
}
}
}
fn check_verbosity(verbosity: usize) {
if verbosity > 4 {
trace!("specifying more than 4 doesn't do anything");
trace!("did you really expect {} different levels?!", verbosity)
}
}
fn setup_fern(level: log::LogLevelFilter) {
match fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"[{}][{}] {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
record.level(),
message
))
})
.level(level)
.chain(std::io::stdout())
.apply() {
Err(_) => {
eprintln!("error setting up logging!");
}
_ => info!("logging set up properly"),
}
}
fn setup_clap() -> (clap::ArgMatches<'static>) {
clap_app!(myapp =>
(name: crate_name!())
(version: crate_version!())
(author: crate_authors!())
(about: crate_description!())
(@arg CONFIG: +required "Set config file to use")
(@arg v: -v --verbose ... "Be verbose (you can add this up to 4 times for more logs)")
).get_matches()
}