Skip to content

Commit

Permalink
Add show-config, show-commands
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaspustina committed Jan 6, 2020
1 parent 6425216 commit 3bb0b5c
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 16 deletions.
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* [x] Find a nice logger
* Profiles
* [X] Config
* [ ] CLI opts: --show-profiles, --show-commands, --profile
* [X] CLI opts: --show-profiles, --show-commands, --profile
* [ ] Create linux and macos configuration
* macOS
* sw_vers
Expand Down
43 changes: 40 additions & 3 deletions src/bin/usereport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ use std::str::FromStr;
use std::sync::mpsc::{self, Receiver, Sender};
use structopt::{StructOpt, clap};
use usereport::{Command, CommandResult, command, report, report::OutputType, runner, Config, Renderer, Report, Runner};
use std::process::exit;
use std::io::Write;

#[derive(Debug, StructOpt)]
#[structopt(name = "usereport", author, about, setting = clap::AppSettings::ColoredHelp)]
struct Opt {
/// Configuration from file, or default if not present
#[structopt(short, long, parse(from_os_str))]
config: Option<PathBuf>,
/// Show active config
#[structopt(long)]
show_config: bool,
/// Output format
#[structopt(short, long, possible_values = &["json", "markdown"], default_value = "markdown")]
output_type: OutputType,
Expand All @@ -30,6 +33,9 @@ struct Opt {
/// Show available profiles
#[structopt(long)]
show_profiles: bool,
/// Show available commands
#[structopt(long)]
show_commands: bool,
}

fn main() -> Result<(), ExitFailure>{
Expand All @@ -50,9 +56,17 @@ fn main() -> Result<(), ExitFailure>{
eprintln!("Using profile '{}'", profile);
}

if opt.show_config {
show_config(&config);
return Ok(())
}
if opt.show_profiles {
show_profiles(&config);
exit(0);
return Ok(())
}
if opt.show_commands {
show_commands(&config);
return Ok(())
}

let commands = config.profile(profile).and_then(|p| config.commands_for_profile(p))?;
Expand All @@ -71,10 +85,19 @@ fn main() -> Result<(), ExitFailure>{
Ok(())
}

fn show_config(config: &Config) {
let toml = toml::to_string_pretty(config)
.expect("failed to serialize active config in TOML");
let stdout = std::io::stdout();
let mut handle = stdout.lock();
handle.write_all(toml.as_bytes())
.expect("failed write TOML to stdout");
}

fn show_profiles(config: &Config) {
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
table.set_titles(row!["Profile", "Commands", "Description"]);
table.set_titles(row!["Name", "Commands", "Description"]);
for p in &config.profiles {
table.add_row(Row::new(vec![
Cell::new(&p.name),
Expand All @@ -85,6 +108,20 @@ fn show_profiles(config: &Config) {
table.printstd();
}

fn show_commands(config: &Config) {
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
table.set_titles(row!["Name", "Command", "Title", "Description"]);
for c in &config.commands {
table.add_row(Row::new(vec![
Cell::new(&c.name()),
Cell::new(&c.args().join(" ")),
Cell::new(&c.title().unwrap_or("-")),
]));
}
table.printstd();
}

fn create_runner<'a>(opt: &Opt, commands: Vec<&'a Command>) -> runner::ThreadRunner<'a> {
if opt.progress {
let tx = create_progress_bar(commands.len());
Expand Down
30 changes: 25 additions & 5 deletions src/command.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::fmt;
use log::{debug, trace};
use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize};
use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use snafu::{ResultExt, Snafu};
use std::{io::Read, time::Duration};
use subprocess::{Popen, PopenConfig, PopenError, Redirection};
Expand Down Expand Up @@ -29,7 +29,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
/// ```
/// # use usereport::command::{Command, CommandResult};
/// let command = Command::new("uname", r#"/usr/bin/uname -a"#, 5)
/// .title("Host OS");
/// .set_title("Host OS");
/// match command.exec() {
/// Ok(CommandResult::Success {
/// command: _,
Expand All @@ -45,7 +45,7 @@ pub struct Command {
pub(crate) name: String,
pub(crate) title: Option<String>,
pub(crate) description: Option<String>,
#[serde(rename = "command", deserialize_with = "de_ser_args")]
#[serde(rename = "command", deserialize_with = "de_ser_args", serialize_with = "ser_args")]
pub(crate) args: Vec<String>,
#[serde(rename = "timeout")]
pub(crate) timeout_sec: u64,
Expand Down Expand Up @@ -76,6 +76,14 @@ where
deserializer.deserialize_string(ArgsVisitor)
}

pub fn ser_args<S>(args: &Vec<String>, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let command: String = args.as_slice().join(" ");
serializer.serialize_str(&command)
}

impl Command {
/// Create new command with default values
pub fn new<T: Into<String>>(name: T, command: T, timeout_sec: u64) -> Command {
Expand All @@ -91,16 +99,28 @@ impl Command {
}
}

/// Get name of command
pub fn name(&self) -> &str { &self.name }

/// Get command args
pub fn args(&self) -> &[String] { self.args.as_slice() }

/// Get title of command
pub fn title(&self) -> Option<&str> { self.title.as_ref().map(|x| x.as_str()) }

/// Get description of command
pub fn description(&self) -> Option<&str> { self.description.as_ref().map(|x| x.as_str()) }

/// Set title of command
pub fn title<T: Into<String>>(self, title: T) -> Command {
pub fn set_title<T: Into<String>>(self, title: T) -> Command {
Command {
title: Some(title.into()),
..self
}
}

/// Set description of command
pub fn description<T: Into<String>>(self, description: T) -> Command {
pub fn set_description<T: Into<String>>(self, description: T) -> Command {
Command {
description: Some(description.into()),
..self
Expand Down
14 changes: 7 additions & 7 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::command::Command;

use serde::Deserialize;
use serde::{Deserialize, Serialize};
use snafu::{ResultExt, Snafu};
use std::{
fs::File,
Expand Down Expand Up @@ -29,7 +29,7 @@ pub enum Error {
/// Result type
pub type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct Config {
pub defaults: Defaults,
pub hostinfo: Option<Hostinfo>,
Expand Down Expand Up @@ -114,7 +114,7 @@ impl Config {
}
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct Defaults {
#[serde(default = "default_profile")]
pub profile: String,
Expand Down Expand Up @@ -145,12 +145,12 @@ fn default_repetitions() -> u64 { Defaults::default().repetitions }

fn default_max_parallel_commands() -> u64 { Defaults::default().max_parallel_commands }

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct Hostinfo {
pub commands: Vec<String>,
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct Profile {
pub name: String,
pub commands: Vec<String>,
Expand Down Expand Up @@ -207,8 +207,8 @@ timeout = 1
let mut commands = Vec::new();
commands.push(
Command::new("uname", "/usr/bin/uname -a", 1)
.title("Host OS")
.description("Basic host OS information")
.set_title("Host OS")
.set_description("Basic host OS information")
);
let expected = Config { defaults, hostinfo: None, profiles, commands };

Expand Down

0 comments on commit 3bb0b5c

Please sign in to comment.