diff --git a/Cargo.lock b/Cargo.lock index 011ec71..d8ef859 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,7 @@ version = "0.1.0" dependencies = [ "build_html", "clap", + "clap_complete", "env_logger", "handlebars", "log", @@ -87,6 +88,15 @@ dependencies = [ "termcolor", ] +[[package]] +name = "clap_complete" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8955d4e8cd4f28f9a01c93a050194c4d131e73ca02f6636bcddbed867014d7" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.1.0" diff --git a/Cargo.toml b/Cargo.toml index db36db3..eef3fb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,5 @@ serde_yaml = "0.9.16" tabled = "0.10.0" build_html = "2.1.0" handlebars = "4.3.1" +clap_complete = "4.0.6" + diff --git a/src/main.rs b/src/main.rs index 7fc20cc..ed082ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ mod connectors; +mod output; mod types; - -use clap::{Parser, ValueEnum}; +use clap::{arg, command, Parser, Subcommand, ValueEnum}; use connectors::{Argo, Connector, Helm, Helmfile}; -use handlebars::Handlebars; use log::{debug, error, info, warn}; +use output::Output; use serde::{Deserialize, Serialize}; use serde_json::from_str; use std::{ @@ -12,7 +12,7 @@ use std::{ io::Result, process::{exit, Command}, }; -use tabled::Tabled; +use types::ExecResult; use version_compare::{Cmp, Version}; use crate::types::{HelmChart, Status}; @@ -24,13 +24,22 @@ enum Kinds { Helmfile, } +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum Outputs { + Yaml, + HTML, +} + /// Check you helm releaseas managed by Argo #[derive(Parser)] #[clap(author, version, about, long_about = None)] struct Args { - /// Type of the + /// How do you install your helm charts #[clap(long, value_enum)] kind: Kinds, + /// What kind of output would you like to receive? + #[clap(long, value_enum, default_value = "yaml")] + output: Outputs, /// Path to the helmfile #[clap(short, long, value_parser, default_value = "./")] path: String, @@ -42,6 +51,14 @@ struct Args { no_sync: bool, } +#[derive(Debug, Subcommand)] +enum Commands { + #[command(arg_required_else_help = true)] + Generate { + #[arg(value_name = "SHELL", default_missing_value = "zsh")] + shell: clap_complete::shells::Shell, + }, +} /// A struct to write helm repo description to #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] struct Repo { @@ -57,25 +74,7 @@ struct LocalCharts { version: Option, } -#[derive(Clone, Tabled, Serialize)] -struct ExecResult { - name: String, - latest_version: String, - current_version: String, - status: Status, -} - // Implementation for the ExecResult struct -impl ExecResult { - fn new(name: String, latest_version: String, current_version: String, status: Status) -> Self { - Self { - name, - latest_version, - current_version, - status, - } - } -} fn main() { // Preparations step @@ -109,7 +108,7 @@ fn main() { // Parse the helmfile // Handling the result - match handle_result(&result, args.outdated_fail) { + match handle_result(&result, args.outdated_fail, args.output) { Ok(result) => { if result { exit(1); @@ -197,7 +196,11 @@ fn check_chart(result: &mut Vec, local_chart: &types::HelmChart) -> } /// Handle result -fn handle_result(result: &Vec, outdated_fail: bool) -> Result { +fn handle_result( + result: &Vec, + outdated_fail: bool, + output_kind: Outputs, +) -> Result { let mut failed = false; for r in result.clone() { match r.status { @@ -220,34 +223,12 @@ fn handle_result(result: &Vec, outdated_fail: bool) -> Result } } } - let template = r#" - - - - - - - - {{#each this as |tr|}} - - - - - - - {{/each}} -
Chart NameCurrent VersionLatest VersionStatus
{{tr.name}}{{tr.current_version}}{{tr.latest_version}}{{tr.status}}
-"#; - let mut reg = Handlebars::new(); - // TODO: Handle this error - reg.register_template_string("html_table", template) - .unwrap(); - - match reg.render("html_table", &result) { - Ok(res) => println!("{}", res), - Err(err) => error!("{}", err), + match output_kind { + Outputs::Yaml => print!("{}", output::YAML::print(result)?), + Outputs::HTML => print!("{}", output::HTML::print(result)?), }; + Ok(failed) } diff --git a/src/output/mod.rs b/src/output/mod.rs new file mode 100644 index 0000000..2423b5b --- /dev/null +++ b/src/output/mod.rs @@ -0,0 +1,61 @@ +use std::io::{Result, Error, ErrorKind}; + +use handlebars::Handlebars; +use log::error; + +use crate::types::ExecResult; + +pub(crate) trait Output { + fn print(data: &Vec) -> Result; +} + +pub(crate) struct HTML; + +impl Output for HTML { + fn print(data: &Vec) -> Result { + // To generate htlm output, I have to use templates because I haven't found any other good + // solution + let template = r#" + + + + + + + + {{#each this as |tr|}} + + + + + + + {{/each}} +
Chart NameCurrent VersionLatest VersionStatus
{{tr.name}}{{tr.current_version}}{{tr.latest_version}}{{tr.status}}
+"#; + + let mut reg = Handlebars::new(); + // TODO: Handle this error + reg.register_template_string("html_table", template) + .unwrap(); + + match reg.render("html_table", &data) { + Ok(res) => Ok(res), + Err(err) => { + error!("{}", err); + return Err(Error::new(ErrorKind::InvalidInput, err.to_string())); + } + } + } +} + +pub(crate) struct YAML; + +impl Output for YAML { + fn print(data: &Vec) -> Result { + match serde_yaml::to_string(&data) { + Ok(res) => return Ok(res), + Err(err) => return Err(Error::new(ErrorKind::InvalidData, err.to_string())), + } + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 768c0de..eaf52b4 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; use std::fmt; +use tabled::Tabled; /// Struct for parsing charts info from helmfile #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] @@ -15,7 +16,7 @@ pub(crate) struct HelmRepo { pub(crate) url: String, } -#[derive(Clone, Serialize)] +#[derive(Clone, Serialize, Deserialize)] pub(crate) enum Status { Uptodate, Outdated, @@ -31,3 +32,22 @@ impl fmt::Display for Status { } } } + +#[derive(Clone, Tabled, Serialize, Deserialize)] +pub(crate) struct ExecResult { + pub(crate) name: String, + pub(crate) latest_version: String, + pub(crate) current_version: String, + pub(crate) status: Status, +} + +impl ExecResult { + pub(crate) fn new(name: String, latest_version: String, current_version: String, status: Status) -> Self { + Self { + name, + latest_version, + current_version, + status, + } + } +}