diff --git a/.gitignore b/.gitignore index 53649aa..e7d56fa 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ Cargo.lock .idea *.tar.gz .vscode -ctr-bundle \ No newline at end of file +ctr-bundle +quardle* +lumper* diff --git a/Cargo.toml b/Cargo.toml index eee4325..77ea363 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "3.0.5", features = ["derive"] } \ No newline at end of file +clap = { version = "3.0.5", features = ["derive"] } +execute = "0.2.10" +flate2 = "1.0.22" +tar = "0.4.38" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +log = { version = "0.4", features = ["std", "serde"] } +env_logger = "0.9.0" \ No newline at end of file diff --git a/src/cli/build.rs b/src/cli/build.rs index 84f6e7a..99257ad 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -23,7 +23,7 @@ pub struct BuildCommand { /// Method that will be called when the command is executed. impl Handler for BuildCommand { - fn handler(&self) -> Result<()> { + fn handler(&self, _logger: &mut env_logger::Builder) -> Result<()> { Ok(()) } } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4ada7fb..da246b6 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -19,13 +19,18 @@ impl From for Error { pub type Result = std::result::Result; pub trait Handler { - fn handler(&self) -> Result<()>; + fn handler(&self, logger: &mut env_logger::Builder) -> Result<()>; } /// Create a cli for quark #[derive(Parser, Debug)] #[clap(version, author)] pub struct Cli { + /// The level of verbosity. + #[clap(short, long, parse(from_occurrences))] + pub(crate) verbose: usize, + + /// The subcommand to apply #[clap(subcommand)] pub(crate) command: Command, } diff --git a/src/cli/run.rs b/src/cli/run.rs index d2b5c0d..37ef9fe 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -1,6 +1,16 @@ +extern crate execute; +use crate::{ + config::{read_config_from_file, QuarkConfig}, + helper::extract_quardle, +}; + use super::{Handler, Result}; use clap::Args; +use std::process::Command; + +use log::{info, LevelFilter}; + /// Arguments for `RunCommand` /// /// Usage : @@ -14,11 +24,40 @@ pub struct RunCommand { /// Folder containing the files generated by the quarks necessary to work #[clap(short, long)] output: String, + + /// If set, the command will be executed silently. + #[clap(long)] + quiet: bool, } /// Method that will be called when the command is executed. impl Handler for RunCommand { - fn handler(&self) -> Result<()> { + fn handler(&self, logger: &mut env_logger::Builder) -> Result<()> { + // Change logger behavior and init it + // If the logger was not initialized, nothing will be displayed into the console. + if self.quiet { + logger.filter_level(LevelFilter::Off); + } + logger.init(); + + extract_quardle(&self.output, &self.quardle)?; + + info!("Start lumper..."); + let quark_path = format!("{}/quark.json", &self.output); + let config: QuarkConfig = read_config_from_file(quark_path).unwrap(); + + let mut child = Command::new("./lumper") + .arg("-k") + .arg(format!("{}/{}", &self.output, config.kernel)) + .arg("--cmdline") + .arg(format!("{}", config.kernel_cmdline)) + .arg("--initramfs") + .arg(format!("{}/{}", &self.output, config.initramfs)) + .spawn() + .expect("lumper faild to start"); + + child.wait().expect("failed to wait on child"); + Ok(()) } } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..af2a2a0 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,26 @@ +use std::{error::Error, fs::File, io::BufReader, path::Path}; + +use serde::Deserialize; + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +pub struct QuarkConfig { + pub quardle: String, + pub kernel: String, + pub initramfs: String, + pub kernel_cmdline: String, + pub image: String, + pub kaps: String, + pub offline: bool, + pub bundle: String, +} + +pub fn read_config_from_file>(path: P) -> Result> { + let file = File::open(path)?; + let reader = BufReader::new(file); + + // Read the JSON contents of the file as an instance of `QuarkConfig`. + let config = serde_json::from_reader(reader)?; + + Ok(config) +} diff --git a/src/helper.rs b/src/helper.rs new file mode 100644 index 0000000..ecc2509 --- /dev/null +++ b/src/helper.rs @@ -0,0 +1,21 @@ +use flate2::read::GzDecoder; +use log::info; +use std::{fs::File, io::Result, path::Path}; +use tar::Archive; + +/// Extract quardle archive to the output directory +pub fn extract_quardle(output: &str, quardle: &str) -> Result<()> { + if !Path::new(output).exists() { + let tar_gz = File::open(quardle)?; + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + + info!("Unpacking quardle..."); + archive.unpack(output)?; + info!("Done"); + } else { + info!("quardle already exists"); + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 00ff48c..f2108b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,38 @@ use clap::StructOpt; use cli::{Cli, Result}; +use log::{log_enabled, Level, LevelFilter}; +use std::io::Write; mod cli; +mod config; +mod helper; fn main() -> Result<()> { let cli: Cli = Cli::parse(); - cli.command().handler()?; + let mut builder = env_logger::Builder::new(); + let logger = builder + .filter_level(match cli.verbose { + 1 => LevelFilter::Debug, + 2 => LevelFilter::Trace, + _ => LevelFilter::Info, + }) + .format(|buf, record| { + if record.level() != Level::Info + || log_enabled!(Level::Trace) + || log_enabled!(Level::Debug) + { + return writeln!( + buf, + "{}: {}", + record.level().to_string().to_lowercase(), + record.args() + ); + } + writeln!(buf, "{}", record.args()) + }); + + cli.command().handler(logger)?; Ok(()) }