From 9f0a6101b89f15f97a6872e16c4e3bb768d162e4 Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Wed, 18 Mar 2020 12:03:07 -0700 Subject: [PATCH] Add configurable color options --- src/bin.rs | 11 ++++---- src/cli/mod.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++--- src/cli/serve.rs | 14 ++++++---- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 2a1336d3..3d1ac733 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -3,12 +3,12 @@ use std::{env, error::Error, panic, process}; use backtrace::Backtrace; use structopt::StructOpt; -use librojo::cli::{self, Options, Subcommand}; +use librojo::cli::{self, GlobalOptions, Options, Subcommand}; -fn run(subcommand: Subcommand) -> Result<(), Box> { +fn run(global: GlobalOptions, subcommand: Subcommand) -> Result<(), Box> { match subcommand { Subcommand::Init(init_options) => cli::init(init_options)?, - Subcommand::Serve(serve_options) => cli::serve(serve_options)?, + Subcommand::Serve(serve_options) => cli::serve(global, serve_options)?, Subcommand::Build(build_options) => cli::build(build_options)?, Subcommand::Upload(upload_options) => cli::upload(upload_options)?, Subcommand::Doc => cli::doc()?, @@ -63,7 +63,7 @@ fn main() { let options = Options::from_args(); - let log_filter = match options.verbosity { + let log_filter = match options.global.verbosity { 0 => "info", 1 => "info,librojo=debug", 2 => "info,librojo=trace", @@ -77,9 +77,10 @@ fn main() { .format_timestamp(None) // Indent following lines equal to the log level label, like `[ERROR] ` .format_indent(Some(8)) + .write_style(options.global.color.into()) .init(); - if let Err(err) = run(options.subcommand) { + if let Err(err) = run(options.global, options.subcommand) { log::error!("{}", err); let mut current_err: &dyn Error = &*err; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 40179a32..1339527c 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -16,6 +16,7 @@ use std::{ }; use structopt::StructOpt; +use thiserror::Error; pub use self::build::*; pub use self::doc::*; @@ -27,16 +28,73 @@ pub use self::upload::*; #[derive(Debug, StructOpt)] #[structopt(name = "Rojo", about, author)] pub struct Options { - /// Sets verbosity level. Can be specified multiple times. - #[structopt(long = "verbose", short, global(true), parse(from_occurrences))] - pub verbosity: u8, + #[structopt(flatten)] + pub global: GlobalOptions, /// Subcommand to run in this invocation. #[structopt(subcommand)] pub subcommand: Subcommand, } -/// All of Rojo's subcommands. +#[derive(Debug, StructOpt)] +pub struct GlobalOptions { + /// Sets verbosity level. Can be specified multiple times. + #[structopt(long("verbose"), short, global(true), parse(from_occurrences))] + pub verbosity: u8, + + /// Set color behavior. Valid values are auto, always, and never. + #[structopt(long("color"), global(true), default_value("auto"))] + pub color: ColorChoice, +} + +#[derive(Debug, Clone, Copy)] +pub enum ColorChoice { + Auto, + Always, + Never, +} + +impl FromStr for ColorChoice { + type Err = ColorChoiceParseError; + + fn from_str(source: &str) -> Result { + match source { + "auto" => Ok(ColorChoice::Auto), + "always" => Ok(ColorChoice::Always), + "never" => Ok(ColorChoice::Never), + _ => Err(ColorChoiceParseError { + attempted: source.to_owned(), + }), + } + } +} + +impl From for termcolor::ColorChoice { + fn from(value: ColorChoice) -> Self { + match value { + ColorChoice::Auto => termcolor::ColorChoice::Auto, + ColorChoice::Always => termcolor::ColorChoice::Always, + ColorChoice::Never => termcolor::ColorChoice::Never, + } + } +} + +impl From for env_logger::WriteStyle { + fn from(value: ColorChoice) -> Self { + match value { + ColorChoice::Auto => env_logger::WriteStyle::Auto, + ColorChoice::Always => env_logger::WriteStyle::Always, + ColorChoice::Never => env_logger::WriteStyle::Never, + } + } +} + +#[derive(Debug, Error)] +#[error("Invalid color choice '{attempted}'. Valid values are: auto, always, never")] +pub struct ColorChoiceParseError { + attempted: String, +} + #[derive(Debug, StructOpt)] pub enum Subcommand { /// Creates a new Rojo project. diff --git a/src/cli/serve.rs b/src/cli/serve.rs index b9ae48b5..b4fbcd63 100644 --- a/src/cli/serve.rs +++ b/src/cli/serve.rs @@ -7,11 +7,15 @@ use anyhow::Result; use memofs::Vfs; use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; -use crate::{cli::ServeCommand, serve_session::ServeSession, web::LiveServer}; +use crate::{ + cli::{GlobalOptions, ServeCommand}, + serve_session::ServeSession, + web::LiveServer, +}; const DEFAULT_PORT: u16 = 34872; -pub fn serve(options: ServeCommand) -> Result<()> { +pub fn serve(global: GlobalOptions, options: ServeCommand) -> Result<()> { let vfs = Vfs::new_default(); let session = Arc::new(ServeSession::new(vfs, &options.absolute_project())); @@ -23,14 +27,14 @@ pub fn serve(options: ServeCommand) -> Result<()> { let server = LiveServer::new(session); - let _ = show_start_message(port); + let _ = show_start_message(port, global.color.into()); server.start(port); Ok(()) } -fn show_start_message(port: u16) -> io::Result<()> { - let writer = BufferWriter::stdout(ColorChoice::Auto); +fn show_start_message(port: u16, color: ColorChoice) -> io::Result<()> { + let writer = BufferWriter::stdout(color); let mut buffer = writer.buffer(); writeln!(&mut buffer, "Rojo server listening:")?;