From 362a81d42fb9e703f70330b007b701812a22aef5 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Fri, 27 Mar 2020 18:41:35 -0700 Subject: [PATCH] =?UTF-8?q?Use=20`strum`=20crate=20to=20derive=20enum?= =?UTF-8?q?=E2=86=94string=20conversions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit type: reform --- Cargo.lock | 20 +++++++++++ Cargo.toml | 2 ++ src/common.rs | 4 ++- src/lint.rs | 60 ++++++++++---------------------- src/options.rs | 5 ++- src/subcommand/torrent/create.rs | 5 ++- src/use_color.rs | 46 ++++++------------------ 7 files changed, 58 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 604a983..15ea099 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,6 +342,8 @@ dependencies = [ "snafu", "static_assertions", "structopt", + "strum", + "strum_macros", "syn", "tempfile", "temptree", @@ -778,6 +780,24 @@ dependencies = [ "syn", ] +[[package]] +name = "strum" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" + +[[package]] +name = "strum_macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "syn" version = "1.0.16" diff --git a/Cargo.toml b/Cargo.toml index 12b3edc..699313e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,8 @@ serde_with = "1.4.0" sha1 = "0.6.0" snafu = "0.6.0" static_assertions = "1.0.0" +strum = "0.18.0" +strum_macros = "0.18.0" syn = "1.0.14" tempfile = "3.0.0" unicode-width = "0.1.0" diff --git a/src/common.rs b/src/common.rs index d88d44a..9603741 100644 --- a/src/common.rs +++ b/src/common.rs @@ -4,7 +4,7 @@ pub(crate) use std::{ char, cmp::{Ordering, Reverse}, collections::{BTreeMap, BTreeSet, HashMap, HashSet}, - convert::{Infallible, TryInto}, + convert::TryInto, env, ffi::{OsStr, OsString}, fmt::{self, Display, Formatter}, @@ -39,6 +39,8 @@ pub(crate) use structopt::{ clap::{AppSettings, ArgSettings}, StructOpt, }; +pub(crate) use strum::VariantNames; +pub(crate) use strum_macros::{EnumString, EnumVariantNames, IntoStaticStr}; pub(crate) use unicode_width::UnicodeWidthStr; pub(crate) use url::{Host, Url}; pub(crate) use walkdir::WalkDir; diff --git a/src/lint.rs b/src/lint.rs index 28b66b2..521b7d3 100644 --- a/src/lint.rs +++ b/src/lint.rs @@ -1,6 +1,9 @@ use crate::common::*; -#[derive(Eq, PartialEq, Debug, Copy, Clone, Ord, PartialOrd)] +#[derive( + Eq, PartialEq, Debug, Copy, Clone, Ord, PartialOrd, EnumVariantNames, IntoStaticStr, EnumString, +)] +#[strum(serialize_all = "kebab-case")] pub(crate) enum Lint { PrivateTrackerless, SmallPieceLength, @@ -8,36 +11,8 @@ pub(crate) enum Lint { } impl Lint { - const PRIVATE_TRACKERLESS: &'static str = "private-trackerless"; - const SMALL_PIECE_LENGTH: &'static str = "small-piece-length"; - const UNEVEN_PIECE_LENGTH: &'static str = "uneven-piece-length"; - pub(crate) const VALUES: &'static [&'static str] = &[ - Self::PRIVATE_TRACKERLESS, - Self::SMALL_PIECE_LENGTH, - Self::UNEVEN_PIECE_LENGTH, - ]; - pub(crate) fn name(self) -> &'static str { - match self { - Self::PrivateTrackerless => Self::PRIVATE_TRACKERLESS, - Self::SmallPieceLength => Self::SMALL_PIECE_LENGTH, - Self::UnevenPieceLength => Self::UNEVEN_PIECE_LENGTH, - } - } -} - -impl FromStr for Lint { - type Err = Error; - - fn from_str(text: &str) -> Result { - match text.replace('_', "-").to_lowercase().as_str() { - Self::PRIVATE_TRACKERLESS => Ok(Self::PrivateTrackerless), - Self::SMALL_PIECE_LENGTH => Ok(Self::SmallPieceLength), - Self::UNEVEN_PIECE_LENGTH => Ok(Self::UnevenPieceLength), - _ => Err(Error::LintUnknown { - text: text.to_string(), - }), - } + self.into() } } @@ -51,22 +26,24 @@ impl Display for Lint { mod tests { use super::*; + #[test] + fn variants() { + assert_eq!( + Lint::VARIANTS, + &[ + "private-trackerless", + "small-piece-length", + "uneven-piece-length" + ] + ); + } + #[test] fn from_str_ok() { - assert_eq!( - Lint::UnevenPieceLength, - "uneven_piece_length".parse().unwrap() - ); - assert_eq!( Lint::UnevenPieceLength, "uneven-piece-length".parse().unwrap() ); - - assert_eq!( - Lint::UnevenPieceLength, - "UNEVEN-piece-length".parse().unwrap() - ); } #[test] @@ -74,6 +51,7 @@ mod tests { fn case(text: &str, value: Lint) { assert_eq!(value, text.parse().unwrap()); assert_eq!(value.name(), text); + assert_eq!(value.to_string(), value.name()); } case("private-trackerless", Lint::PrivateTrackerless); @@ -85,7 +63,7 @@ mod tests { fn from_str_err() { assert_matches!( "foo".parse::(), - Err(Error::LintUnknown { text }) if text == "foo" + Err(strum::ParseError::VariantNotFound) ); } } diff --git a/src/options.rs b/src/options.rs index 5d5e93d..d25e0d2 100644 --- a/src/options.rs +++ b/src/options.rs @@ -14,9 +14,8 @@ pub(crate) struct Options { #[structopt( long = "color", value_name = "WHEN", - default_value = UseColor::AUTO, - set = ArgSettings::CaseInsensitive, - possible_values = UseColor::VALUES, + default_value = UseColor::Auto.into(), + possible_values = UseColor::VARIANTS, help = "Print colorful output according to `WHEN`. When `auto`, the default, colored output \ is only enabled if imdl detects that it is connected to a terminal, the `NO_COLOR` \ environment variable is not set, and the `TERM` environment variable is not set to \ diff --git a/src/subcommand/torrent/create.rs b/src/subcommand/torrent/create.rs index 77a5eba..3f76c70 100644 --- a/src/subcommand/torrent/create.rs +++ b/src/subcommand/torrent/create.rs @@ -22,8 +22,7 @@ pub(crate) struct Create { long = "allow", short = "A", value_name = "LINT", - possible_values = Lint::VALUES, - set(ArgSettings::CaseInsensitive), + possible_values = Lint::VARIANTS, help = "Allow `LINT`. Lints check for conditions which, although permitted, are not usually \ desirable. For example, piece length can be any non-zero value, but probably \ shouldn't be below 16 KiB. The lint `small-piece-size` checks for this, and \ @@ -2254,7 +2253,7 @@ Content Size 9 bytes "foo", "--private", "--allow", - "private-trackerLESS", + "private-trackerless", ], tree: { foo: "", diff --git a/src/use_color.rs b/src/use_color.rs index 1591787..3f51591 100644 --- a/src/use_color.rs +++ b/src/use_color.rs @@ -1,52 +1,26 @@ use crate::common::*; -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, EnumVariantNames, EnumString, IntoStaticStr)] +#[strum(serialize_all = "kebab-case")] pub(crate) enum UseColor { Auto, Always, Never, } -impl UseColor { - pub(crate) const ALWAYS: &'static str = "always"; - pub(crate) const AUTO: &'static str = "auto"; - pub(crate) const NEVER: &'static str = "never"; - pub(crate) const VALUES: &'static [&'static str] = &[Self::AUTO, Self::ALWAYS, Self::NEVER]; -} - -impl FromStr for UseColor { - type Err = Infallible; - - fn from_str(text: &str) -> Result { - match text.to_lowercase().as_str() { - Self::AUTO => Ok(Self::Auto), - Self::ALWAYS => Ok(Self::Always), - Self::NEVER => Ok(Self::Never), - _ => unreachable!(), - } - } -} - #[cfg(test)] mod tests { use super::*; + #[test] + fn variants() { + assert_eq!(UseColor::VARIANTS, &["auto", "always", "never"]); + } + #[test] fn from_str() { - assert_eq!(UseColor::Auto, UseColor::AUTO.parse().unwrap()); - assert_eq!(UseColor::Always, UseColor::ALWAYS.parse().unwrap()); - assert_eq!(UseColor::Never, UseColor::NEVER.parse().unwrap()); - assert_eq!( - UseColor::Auto, - UseColor::AUTO.to_uppercase().parse().unwrap() - ); - assert_eq!( - UseColor::Always, - UseColor::ALWAYS.to_uppercase().parse().unwrap() - ); - assert_eq!( - UseColor::Never, - UseColor::NEVER.to_uppercase().parse().unwrap() - ); + assert_eq!(UseColor::Auto, "auto".parse().unwrap()); + assert_eq!(UseColor::Always, "always".parse().unwrap()); + assert_eq!(UseColor::Never, "never".parse().unwrap()); } }