From 9ce37a975ad4889e070ae5705b6750a4b33d2442 Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Fri, 12 Nov 2021 01:38:45 +0100 Subject: More colors! The user_choice() function was a bit weird because it needed a "formatted" item for every item to select (not using the Display of the item), but it also didn't support colored output. This change fixes that by introducing a new trait that does colored output and by making user_choice() use that trait to output the lines. --- src/fancyfmt.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/install.rs | 13 ++----------- src/main.rs | 9 +++++++-- src/uiutil.rs | 20 ++++++++++---------- 4 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 src/fancyfmt.rs (limited to 'src') diff --git a/src/fancyfmt.rs b/src/fancyfmt.rs new file mode 100644 index 0000000..6c631b3 --- /dev/null +++ b/src/fancyfmt.rs @@ -0,0 +1,55 @@ +//! This module provides some methods for "fancily" formatting data. +//! +//! This mainly exists so that we can have a version of [`std::fmt::Display`] that can use the +//! colors provided by [`termcolor`]. +use std::io::Write; + +use anyhow::Result; +use modderbaas::{contentdb::ContentMeta, World}; +use termcolor::{Color, ColorSpec, StandardStream, WriteColor}; + +/// A trait for objects to output them to a (possibly) colored stream. +pub trait ColoredDisplay { + /// Output the object to the colored stream. + fn fmt(&self, output: &mut StandardStream) -> Result<()>; +} + +impl ColoredDisplay for &T { + fn fmt(&self, output: &mut StandardStream) -> Result<()> { + (*self).fmt(output) + } +} + +impl ColoredDisplay for &str { + fn fmt(&self, output: &mut StandardStream) -> Result<()> { + write!(output, "{}", *self)?; + Ok(()) + } +} + +impl ColoredDisplay for String { + fn fmt(&self, output: &mut StandardStream) -> Result<()> { + (self as &str).fmt(output) + } +} + +impl ColoredDisplay for World { + fn fmt(&self, output: &mut StandardStream) -> Result<()> { + write!(output, "{}", self)?; + Ok(()) + } +} + +impl ColoredDisplay for ContentMeta { + fn fmt(&self, output: &mut StandardStream) -> Result<()> { + output.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?; + write!(output, "{}", self.title)?; + output.reset()?; + write!(output, " by ")?; + output.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)))?; + write!(output, "{}", self.author)?; + output.reset()?; + write!(output, " - {}", self.short_description)?; + Ok(()) + } +} diff --git a/src/install.rs b/src/install.rs index 56754d3..e34ffc3 100644 --- a/src/install.rs +++ b/src/install.rs @@ -69,21 +69,12 @@ impl<'o, 'p> Installer for InteractiveInstaller<'o, 'p> { } else if candidates.len() == 1 { Ok(Source::Http(candidates.into_iter().next().unwrap().url)) } else { - let items = candidates - .into_iter() - .map(|c| { - ( - format!("{} by {} - {}", c.title, c.author, c.short_description), - c, - ) - }) - .collect::>(); writeln!( &mut self.output, "{} candidates found, please select one:", - items.len() + candidates.len() )?; - let candidate = user_choice(&items, self.output)?; + let candidate = user_choice(&candidates, self.output)?; Ok(Source::Http(candidate.url.clone())) } } diff --git a/src/main.rs b/src/main.rs index 5390479..20e8653 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,8 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use modderbaas::{baas::Baas, MineMod, Snapshot, World}; +mod fancyfmt; + mod uiutil; use uiutil::{ask_continue, user_choice}; @@ -137,8 +139,11 @@ fn select_world( } // Here, we cannot do an automatic selection, so ask the user: - let mut worlds = worlds.iter().collect::>(); - worlds.sort_by_key(|i| i.0); + let worlds = worlds + .iter() + .sorted_by_key(|i| i.0) + .map(|i| i.1) + .collect::>(); writeln!( output, "The following worlds were found, please select one:" diff --git a/src/uiutil.rs b/src/uiutil.rs index 188e20c..0cc88a9 100644 --- a/src/uiutil.rs +++ b/src/uiutil.rs @@ -1,22 +1,22 @@ //! Utility functions for user interaction. -use std::{ - fmt::Display, - io::{self, Write}, -}; +use std::io::{self, Write}; use anyhow::{bail, Result}; use termcolor::{Color, ColorSpec, StandardStream, WriteColor}; +use super::fancyfmt::ColoredDisplay; + /// Presents the user with a choice of items and awaits a selection. -pub fn user_choice<'i, L: Display, I>( - items: &'i [(L, I)], +pub fn user_choice<'i, I: ColoredDisplay>( + items: &'i [I], output: &mut StandardStream, ) -> Result<&'i I> { - for (i, (label, _)) in items.iter().enumerate() { + for (i, item) in items.iter().enumerate() { output.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?; - write!(output, "[{}]", i)?; + write!(output, "[{}] ", i)?; output.reset()?; - writeln!(output, " {}", label)?; + item.fmt(output)?; + writeln!(output)?; } let stdin = io::stdin(); @@ -27,7 +27,7 @@ pub fn user_choice<'i, L: Display, I>( stdin.read_line(&mut buffer)?; if let Ok(number) = buffer.trim().parse::() { if number < items.len() { - return Ok(&items[number].1); + return Ok(&items[number]); } } } -- cgit v1.2.3