Repository: pacak/bpaf
Pull request #97 (2022-10-02 12:21:59)

Drop FromOsStr trait
Plus some minor changes


Patch:
diff --git a/Changelog.md b/Changelog.md
index a473f73..f5bd077 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,9 +1,22 @@
 # Change Log
 
-## bpaf [0.6.2] - unreleased
+## bpaf [0.7.0] - unreleased
 - cosmetic improvements
 - pure_with implementation
   thanks to @xitep
+- FromOsStr is replaced with magical uses of Any trait
+
+# Migration guide 0.6.x -> 0.7.x
+1. Remove FromUtf8 annotations if you have any
+   ```diff
+   -let coin = short('c').argument::<FromUtf8<Coin>>("COIN");
+   +let coin = short('c').argument::<Coin>("COIN");
+   ```
+   In many cases rustc should be able to derive what the type
+2. Replace FromOsStr implementations for your types with FromStr
+   if you have any. If your type requires parsing OsString directly
+   you can perform it in two steps - consuming OsString + parsing it
+   with `Parser::parse`
 
 ## bpaf [0.6.1] - 2022-09-30
 - cosmetic improvements
diff --git a/docs/src/argument/combine.rs b/docs/src/argument/combine.rs
index 649fb3e..7833fb5 100644
--- a/docs/src/argument/combine.rs
+++ b/docs/src/argument/combine.rs
@@ -10,8 +10,10 @@ pub struct Options {
 
 pub fn options() -> OptionParser<Options> {
     let value = long("value").argument::<isize>("ARG").fallback(100);
-    // You can use FromUtf8 type tag to parse things that only implement `FromStr`, but not `FromOsStr`
-    // `u64` implements both and only used as an example
-    let shorty = short('s').argument::<FromUtf8<u64>>("ARG");
+    // in many cases rustc is able to deduct exact type for the argument
+    // you are trying to consume, alternatively you can always specify it
+    // with turbofish to `argument:`
+    // let shorty = short('s').argument::<u64>("ARG");
+    let shorty = short('s').argument("ARG");
     construct!(Options { value, shorty }).to_options()
 }
diff --git a/docs/src/argument/derive.rs b/docs/src/argument/derive.rs
index 1157607..63f9eb9 100644
--- a/docs/src/argument/derive.rs
+++ b/docs/src/argument/derive.rs
@@ -7,8 +7,10 @@ use bpaf::*;
 pub struct Options {
     #[bpaf(fallback(100))]
     value: isize,
-    // You can use FromUtf8 type tag to parse things that only implement FromStr, but not FromOsStr
-    // `u64` implements both and only used as an example
-    #[bpaf(short, argument::<FromUtf8<u64>>("ARG"))]
+    // in many cases rustc is able to deduct exact type for the argument
+    // you are trying to consume, alternatively you can always specify it
+    // with turbofish to `argument:`
+    // #[bpaf(short, argument::<u64>("ARG"))]
+    #[bpaf(short, argument("ARG"))]
     shorty: u64,
 }
diff --git a/docs/src/positional/cases.txt b/docs/src/positional/cases.txt
index 3a49571..50db597 100644
--- a/docs/src/positional/cases.txt
+++ b/docs/src/positional/cases.txt
@@ -8,8 +8,7 @@ Options { coin: Heads, file: "main.rs", name: None }
 OK
 Options { coin: Heads, file: "main.rs", name: Some("hello") }
 
-? To parse items without having to write `FromOsStr` instance you can use `FromUtf8` helper
-? type?
+? You can consume items of any type that implements `FromStr`.
 > main.rs --coin tails
 OK
 Options { coin: Tails, file: "main.rs", name: None }
diff --git a/docs/src/positional/combine.rs b/docs/src/positional/combine.rs
index 7739edd..ee1fe30 100644
--- a/docs/src/positional/combine.rs
+++ b/docs/src/positional/combine.rs
@@ -11,7 +11,7 @@ pub struct Options {
     name: Option<String>,
 }
 
-/// A custom datatype that doesn't implement [`FromOsStr`] but implements [`FromStr`]
+/// A custom datatype that implements [`FromStr`]
 #[derive(Debug, Clone, Copy)]
 enum Coin {
     Heads,
@@ -33,7 +33,7 @@ pub fn options() -> OptionParser<Options> {
     // sometimes you can get away with not specifying type in positional's turbofish
     let coin = long("coin")
         .help("Coin toss results")
-        .argument::<FromUtf8<Coin>>("COIN")
+        .argument::<Coin>("COIN")
         .fallback(Coin::Heads);
     let name = positional::<String>("NAME")
         .help("Name to look for")
diff --git a/docs/src/positional/derive.rs b/docs/src/positional/derive.rs
index 13b9895..f50734e 100644
--- a/docs/src/positional/derive.rs
+++ b/docs/src/positional/derive.rs
@@ -8,7 +8,7 @@ use bpaf::*;
 #[bpaf(options)]
 pub struct Options {
     /// Coin toss results
-    #[bpaf(argument::<FromUtf8<Coin>>("COIN"), fallback(Coin::Heads))]
+    #[bpaf(argument("COIN"), fallback(Coin::Heads))]
     coin: Coin,
 
     /// File to use
@@ -19,7 +19,7 @@ pub struct Options {
     name: Option<String>,
 }
 
-/// A custom datatype that doesn't implement [`FromOsStr`] but implements [`FromStr`]
+/// A custom datatype that implements [`FromStr`]
 #[derive(Debug, Clone, Copy)]
 enum Coin {
     Heads,
diff --git a/examples/enum_in_args.rs b/examples/enum_in_args.rs
index 3913879..50f3694 100644
--- a/examples/enum_in_args.rs
+++ b/examples/enum_in_args.rs
@@ -1,5 +1,7 @@
 //! parsing argument value into enum. You can use crate `strum`'s `EnumString` for this purposes as well
 //!
+use std::str::FromStr;
+
 use bpaf::*;
 
 #[derive(Debug, Clone)]
@@ -9,15 +11,12 @@ enum Baz {
     FooBar,
 }
 
-impl FromOsStr for Baz {
-    type Out = Self;
-    fn from_os_str(s: std::ffi::OsString) -> Result<Self, String>
+impl FromStr for Baz {
+    type Err = String;
+    fn from_str(s: &str) -> Result<Self, String>
     where
         Self: Sized,
     {
-        let s = s
-            .to_str()
-            .ok_or_else(|| format!("{} is not a valid utf8", s.to_string_lossy()))?;
         match s {
             "foo" => Ok(Baz::Foo),
             "bar" => Ok(Baz::Bar),
diff --git a/src/_applicative.rs b/src/_applicative.rs
new file mode 100644
index 0000000..72daa4c
--- /dev/null
+++ b/src/_applicative.rs
@@ -0,0 +1,111 @@
+//! # Applicative functors? What is it about?
+//!
+//! You don't need to read/understand this chapter in order to use the library but it might
+//! help to understand what makes it tick.
+
+//! ## Functors
+//!
+//! Let's start by talking about what a `Functor` is. In Haskell it is defined as
+//!
+//! ```haskell
+//! class Functor f where
+//!     fmap :: (a -> b) -> f a -> f b
+//! ```
+//!
+//! looks scary, but in Rust terms it's a trait that takes a *value in a context* or a container such as
+//! `Option<A>` and a function `fn(A) -> B` and gives you `Option<B>` back.
+//!
+//! Closest analogy in a real code you can write in Rust right now would be modifying an `Option`
+//! using only `Option::map`:
+//! ```rust
+//! fn plus_one(input: Option<u32>) -> Option<u32> {
+//!     input.map(|i| i + 1)
+//! }
+//!
+//! let present = Some(10);
+//! let absent = None;
+//!
+//! assert_eq!(plus_one(present), Some(11));
+//! assert_eq!(plus_one(absent), None);
+//! ```
+//!
+//! `Vec`, `Result` and other types that implement `map` are Functors as well, but functors are not
+//! limited just to containers - you don't have to have a value inside to be able to manipulate it.
+//! Consider `Reader` that allows you to perform pure transformations on a *value in a context* `T`
+//! without having any value until it's ready to execute:
+//!
+//! ```rust
+//! # use std::marker::PhantomData;
+//! struct Reader<T>(PhantomData<T>, Box<dyn Fn(T) -> T>);
+//!
+//! impl<T> Reader<T> {
+//!     /// Initialize an new value in a context
+//!     fn new() -> Self {
+//!         Self(PhantomData, Box::new(|x| x))
+//!     }
+//!
+//!     /// Modify a value in a context
+//!     fn map<F>(self, f: F) -> Self
+//!     where
+//!         F: Fn(T) -> T + 'static,
+//!         T: 'static,
+//!     {
+//!         Self(
+//!             PhantomData,
+//!             Box::new(move |x| {
+//!                 let inner = &self.1;
+//!                 f(inner(x))
+//!             }),
+//!         )
+//!     }
+//!
+//!     /// Apply the changes by giving it the initial value
+//!     fn run(self, input: T) -> T {
+//!         let f = &self.1;
+//!         f(input)
+//!     }
+//! }
+//!
+//! let val = Reader::<u32>::new();
+//! let val = val.map(|x| x + 1);
+//! let res = val.run(10);
+//! assert_eq!(res, 11);
+//! ```
+
+//! ## Applicative Functors
+//!
+//! `map` in `Functors` is limited to a single value, `Applicative Functors` extend it to multiple values, closest Rust
+//! analogy would be doing computations on `Option` or `Result` using only `?` and `Some`/`Ok` but
+//! without making any decisions on input values after they been extracted from `Option`.
+//! ```rust
+//! fn add_numbers(input_a: Option<u32>, input_b: Option<u32>) -> Option<u32> {
+//!     Some(input_a? + input_b?)
+//! }
+//!
+//! let present_1 = Some(10);
+//! let present_2 = Some(20);
+//! let absent = None;
+//!
+//! assert_eq!(add_numbers(present_1, present_2), Some(30));
+//! assert_eq!(add_numbers(present_1, absent), None);
+//! assert_eq!(add_numbers(absent, absent), None);
+//! ```
+//!
+//! Similarly to `Functors`, `Applicative Functors` are not limited to containers and can
+//! represent *a value in an arbitrary context*.
+//!
+//! `Try` trait (`?`) for `Option` and `Result` short circuits when it finds a missing value,
+//! but `Applicative Functors` in general don't have to - in fact to implement dynamic completion
+//! `bpaf` needs to check items past the first failure point to collect all the possible
+//! completions.
+//!
+
+//! ## `Parser` trait
+//!
+//! Similarly to `Reader` struct described above individual parsers don't actually contain a value
+//! but only a value in context - they contain a computation describing the initial consumer plus
+//! all the transformations and validations layered on top. [`construct!`] macro takes several
+//! parser objects and composes them similarly to `add_numbers` function above.
+
+#[cfg(doc)]
+use crate::construct;
diff --git a/src/_derive_tutorial.rs b/src/_derive_tutorial.rs
index cf5d94e..b9eac34 100644
--- a/src/_derive_tutorial.rs
+++ b/src/_derive_tutorial.rs
@@ -188,9 +188,9 @@
 //!      and `Vec<Foo>` with [`many`](Parser::many), see `postprocessing` for more details.
 //!
 //!      + `bpaf_derive` handles `bool` fields with [`switch`](NamedArg::switch),
-//!      `()` with [`req_flag`](NamedArg::req_flag) and anything else with [`FromOsStr`]
-//!      trait. See documentation for [`argument`](NamedArg::argument) and [`positional`]
-//!      for more details.
+//!      `()` with [`req_flag`](NamedArg::req_flag), [`OsString`] and [`PathBuf`] are parsed from
+//!      `OsString` and anything else with [`FromStr`] trait. See documentation for
+//!      [`argument`](NamedArg::argument) and [`positional`] for more details.
 //!
 //!    - If `external` is present - it usually serves function of `naming` and `consumer`, allowing
 //!      more for `postprocessing` annotations after it. Takes an optional parameter - a function
@@ -278,6 +278,8 @@
 
 #[cfg(doc)]
 use crate::{params::*, parsers::*, *};
+#[cfg(doc)]
+use std::{ffi::OsString, path::PathBuf};
 
 #[cfg(all(doc, feature = "batteries"))]
 use crate::batteries::cargo_helper;
diff --git a/src/docs/argument.md b/src/docs/argument.md
index 94a7e1d..f7e02e9 100644
--- a/src/docs/argument.md
+++ b/src/docs/argument.md
@@ -12,9 +12,11 @@ pub struct Options {
 
 pub fn options() -> OptionParser<Options> {
     let value = long("value").argument::<isize>("ARG").fallback(100);
-    // You can use FromUtf8 type tag to parse things that only implement `FromStr`, but not `FromOsStr`
-    // `u64` implements both and only used as an example
-    let shorty = short('s').argument::<FromUtf8<u64>>("ARG");
+    // in many cases rustc is able to deduct exact type for the argument
+    // you are trying to consume, alternatively you can always specify it
+    // with turbofish to `argument:`
+    // let shorty = short('s').argument::<u64>("ARG");
+    let shorty = short('s').argument("ARG");
     construct!(Options { value, shorty }).to_options()
 }
 ```
@@ -31,9 +33,11 @@ pub fn options() -> OptionParser<Options> {
 pub struct Options {
     #[bpaf(fallback(100))]
     value: isize,
-    // You can use FromUtf8 type tag to parse things that only implement FromStr, but not FromOsStr
-    // `u64` implements both and only used as an example
-    #[bpaf(short, argument::<FromUtf8<u64>>("ARG"))]
+    // in many cases rustc is able to deduct exact type for the argument
+    // you are trying to consume, alternatively you can always specify it
+    // with turbofish to `argument:`
+    // #[bpaf(short, argument::<u64>("ARG"))]
+    #[bpaf(short, argument("ARG"))]
     shorty: u64,
 }
 ```
diff --git a/src/docs/positional.md b/src/docs/positional.md
index 8af9fd9..1c8678f 100644
--- a/src/docs/positional.md
+++ b/src/docs/positional.md
@@ -12,7 +12,7 @@ pub struct Options {
     name: Option<String>,
 }
 
-/// A custom datatype that doesn't implement [`FromOsStr`] but implements [`FromStr`]
+/// A custom datatype that implements [`FromStr`]
 #[derive(Debug, Clone, Copy)]
 enum Coin {
     Heads,
@@ -34,7 +34,7 @@ pub fn options() -> OptionParser<Options> {
     // sometimes you can get away with not specifying type in positional's turbofish
     let coin = long("coin")
         .help("Coin toss results")
-        .argument::<FromUtf8<Coin>>("COIN")
+        .argument::<Coin>("COIN")
         .fallback(Coin::Heads);
     let name = positional::<String>("NAME")
         .help("Name to look for")
@@ -55,7 +55,7 @@ pub fn options() -> OptionParser<Options> {
 #[bpaf(options)]
 pub struct Options {
     /// Coin toss results
-    #[bpaf(argument::<FromUtf8<Coin>>("COIN"), fallback(Coin::Heads))]
+    #[bpaf(argument("COIN"), fallback(Coin::Heads))]
     coin: Coin,
 
     /// File to use
@@ -66,7 +66,7 @@ pub struct Options {
     name: Option<String>,
 }
 
-/// A custom datatype that doesn't implement [`FromOsStr`] but implements [`FromStr`]
+/// A custom datatype that implements [`FromStr`]
 #[derive(Debug, Clone, Copy)]
 enum Coin {
     Heads,
@@ -101,8 +101,7 @@ Both positionals are present
 Options { coin: Heads, file: "main.rs", name: Some("hello") }
 ```
 
-To parse items without having to write `FromOsStr` instance you can use `FromUtf8` helper
-type?
+You can consume items of any type that implements `FromStr`.
 ```console
 % app main.rs --coin tails
 Options { coin: Tails, file: "main.rs", name: None }
diff --git a/src/from_os_str.rs b/src/from_os_str.rs
index f81806b..1e89fe2 100644
--- a/src/from_os_str.rs
+++ b/src/from_os_str.rs
@@ -1,130 +1,28 @@
-#[cfg(doc)]
-use crate::{any, positional, NamedArg};
-use std::{
-    ffi::{OsStr, OsString},
-    marker::PhantomData,
-    path::PathBuf,
-    str::FromStr,
-};
+use std::{ffi::OsString, path::PathBuf, str::FromStr};
 
-/// Like [`FromStr`] but parses [`OsString`] instead
-///
-/// `bpaf` implements it for most of the things in std lib supported by [`FromStr`]. you can implement it for
-/// your types to be able to use them in turbofish directly for [`positional`],
-/// [`argument`](NamedArg::argument) and [`any`].
-///
-/// Alternatively you can use [`FromUtf8`] type tag to parse any type that implements [`FromStr`]
-#[doc = include_str!("docs/positional.md")]
-///
-pub trait FromOsStr {
-    /// Mostly a hack to allow parsing types that implement inside [`FromStr`] but aren't
-    /// contained inside stdlib, see [`FromUtf8`].
-    type Out;
-
-    /// Parse [`OsString`] or fail
-    ///
-    /// # Errors
-    /// Returns error message along with a lossy representation of the original string on failure
-    fn from_os_str(s: OsString) -> Result<Self::Out, String>
-    where
-        Self: Sized;
-}
-
-/// A tag datatype that allows to use [`FromStr`] trait inside [`FromOsStr`].
-///
-/// See [`FromOsStr`] for more details
-pub struct FromUtf8<T>(PhantomData<T>);
-
-impl<T> FromOsStr for FromUtf8<T>
+pub(crate) fn parse_os_str<T>(os: OsString) -> Result<T, String>
 where
-    T: FromStr,
+    T: FromStr + 'static,
     <T as std::str::FromStr>::Err: std::fmt::Display,
 {
-    type Out = T;
-
-    fn from_os_str(os: OsString) -> Result<Self::Out, String>
-    where
-        Self: Sized,
-    {
-        let s = as_str(&os)?;
-        match T::from_str(s) {
-            Ok(t) => Ok(t),
-            Err(err) => Err(err.to_string()),
+    use std::any::Any;
+    use std::any::*;
+
+    if TypeId::of::<T>() == TypeId::of::<OsString>() {
+        let anybox: Box<dyn Any> = Box::new(os);
+        Ok(*(anybox.downcast::<T>().unwrap()))
+    } else if TypeId::of::<T>() == TypeId::of::<PathBuf>() {
+        let anybox: Box<dyn Any> = Box::new(PathBuf::from(os));
+        Ok(*(anybox.downcast::<T>().unwrap()))
+    } else {
+        match os.to_str() {
+            Some(s) => T::from_str(s).map_err(|e| e.to_string()),
+            None => Err(format!("{} is not a valid utf8", os.to_string_lossy())),
         }
     }
 }
 
-macro_rules! from_os_str {
-    ($ty:ty) => {
-        impl FromOsStr for $ty {
-            type Out = Self;
-            fn from_os_str(s: OsString) -> Result<Self::Out, String> {
-                match <$ty as FromStr>::from_str(as_str(&s)?) {
-                    Ok(ok) => Ok(ok),
-                    Err(err) => Err(err.to_string()),
-                }
-            }
-        }
-    };
-}
-
-impl FromOsStr for OsString {
-    type Out = Self;
-    fn from_os_str(s: OsString) -> Result<Self::Out, String> {
-        Ok(s)
-    }
-}
-impl FromOsStr for PathBuf {
-    type Out = Self;
-    fn from_os_str(s: OsString) -> Result<Self::Out, String> {
-        Ok(Self::from(s))
-    }
-}
-
-from_os_str!(bool);
-from_os_str!(char);
-from_os_str!(f32);
-from_os_str!(f64);
-from_os_str!(i128);
-from_os_str!(i16);
-from_os_str!(i32);
-from_os_str!(i64);
-from_os_str!(i8);
-from_os_str!(isize);
-from_os_str!(String);
-from_os_str!(u128);
-from_os_str!(u16);
-from_os_str!(u32);
-from_os_str!(u64);
-from_os_str!(u8);
-from_os_str!(usize);
-
-from_os_str!(std::net::Ipv4Addr);
-from_os_str!(std::net::Ipv6Addr);
-from_os_str!(std::net::SocketAddr);
-from_os_str!(std::net::SocketAddrV4);
-from_os_str!(std::net::SocketAddrV6);
-from_os_str!(std::num::NonZeroI128);
-from_os_str!(std::num::NonZeroI16);
-from_os_str!(std::num::NonZeroI32);
-from_os_str!(std::num::NonZeroI64);
-from_os_str!(std::num::NonZeroI8);
-from_os_str!(std::num::NonZeroIsize);
-from_os_str!(std::num::NonZeroU128);
-from_os_str!(std::num::NonZeroU16);
-from_os_str!(std::num::NonZeroU32);
-from_os_str!(std::num::NonZeroU64);
-from_os_str!(std::num::NonZeroU8);
-from_os_str!(std::num::NonZeroUsize);
-
-#[inline(never)]
-/// Try to use `&OsStr` as `&str`
-///
-/// # Errors
-/// Returns error message along with a lossy representation of the original string on failure
-fn as_str(os: &OsStr) -> Result<&str, String> {
-    match os.to_str() {
-        Some(s) => Ok(s),
-        None => Err(format!("{} is not a valid utf8", os.to_string_lossy())),
-    }
-}
+#[doc(hidden)]
+/// This is a no-op and only exists to avoid breaking existing code
+/// that relies on it. I'll deprecate/drop it somewhere in 2023
+pub type FromUtf8<T> = T;
diff --git a/src/item.rs b/src/item.rs
index 7ab9961..3048cdd 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -48,7 +48,6 @@ impl From<&NamedArg> for ShortLong {
 }
 
 /// {} renders a version for short usage string
-/// {:#} renders a full width version for --help body and complete, this version
 /// supports padding of the help by some max width
 impl std::fmt::Display for Item {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
diff --git a/src/lib.rs b/src/lib.rs
index e598aac..1644fd7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -20,6 +20,7 @@
 //! - [Derive tutorial](crate::_derive_tutorial)
 //! - [Combinatoric tutorial](crate::_combinatoric_tutorial)
 //! - [Some very unusual cases](crate::_unusual)
+//! - [Applicative functors? What is it all about](crate::_applicative)
 // - [Picking the right words](crate::_flow)
 //! - [Batteries included](crate::batteries)
 //! - [Q&A](https://github.com/pacak/bpaf/discussions/categories/q-a)
@@ -311,6 +312,8 @@
 //!
 //! - `autocomplete`: enables support for shell autocompletion. Disabled by default.
 
+#[cfg(feature = "extradocs")]
+pub mod _applicative;
 #[cfg(feature = "extradocs")]
 pub mod _combinatoric_tutorial;
 #[cfg(feature = "extradocs")]
@@ -365,7 +368,7 @@ use structs::{ParseComp, ParseCompStyle};
 
 #[doc(inline)]
 pub use crate::args::Args;
-pub use crate::from_os_str::{FromOsStr, FromUtf8};
+pub use crate::from_os_str::FromUtf8;
 pub use crate::info::OptionParser;
 pub use crate::meta::Meta;
 
@@ -640,7 +643,7 @@ use std::str::FromStr;
 /// Values inside can be of any type for as long as they implement `Debug`, `Clone` and
 /// there's no lifetimes other than static.
 ///
-/// When consuming the values you can jump straight to a value that implements either [`FromOsStr`] or
+/// When consuming the values you can jump straight to a value that implements
 /// [`FromStr`] trait then transform into something that your program would actually use. Alternatively
 /// you can consume either `String` or `OsString` and parse that by hand. It's better to perform
 /// as much parsing and validation inside the `Parser` as possible so the program itself gets
diff --git a/src/meta_help.rs b/src/meta_help.rs
index 5b57d8f..290c6b9 100644
--- a/src/meta_help.rs
+++ b/src/meta_help.rs
@@ -335,7 +335,11 @@ pub(crate) fn render_help(
     let auto = parser_meta.as_usage_meta().map(|u| u.to_string());
     if let Some(custom_usage) = info.usage {
         match auto {
-            Some(auto_usage) => write!(res, "{}\n", custom_usage.replace("{usage}", &auto_usage)),
+            Some(auto_usage) => write!(
+                res,
+                "{}\n",
+                custom_usage.replacen("{usage}", &auto_usage, 1)
+            ),
             None => write!(res, "{}\n", custom_usage),
         }?;
     } else if let Some(usage) = auto {
@@ -360,7 +364,7 @@ pub(crate) fn render_help(
             .max()
             .unwrap_or(0);
         write!(res, "\nAvailable positional items:\n")?;
-        for i in items.psns {
+        for i in &items.psns {
             write!(res, "{:padding$}\n", i, padding = max_width)?;
         }
     }
@@ -373,7 +377,7 @@ pub(crate) fn render_help(
             .max()
             .unwrap_or(0);
         write!(res, "\nAvailable options:\n")?;
-        for i in items.flgs {
+        for i in &items.flgs {
             write!(res, "{:padding$}\n", i, padding = max_width)?;
         }
     }
@@ -386,7 +390,7 @@ pub(crate) fn render_help(
             .map(HelpItem::full_width)
             .max()
             .unwrap_or(0);
-        for i in items.cmds {
+        for i in &items.cmds {
             write!(res, "{:padding$}\n", i, padding = max_width)?;
         }
     }
diff --git a/src/meta_youmean.rs b/src/meta_youmean.rs
index 06cd021..26e22e8 100644
--- a/src/meta_youmean.rs
+++ b/src/meta_youmean.rs
@@ -6,8 +6,7 @@ use crate::{
 
 pub(crate) fn should_suggest(err: &Error) -> bool {
     match err {
-        Error::Stdout(_) => false,
-        Error::Stderr(_) => true,
+        Error::Stdout(_) | Error::Stderr(_) => true,
         Error::Missing(xs) => {
             let mut hi = crate::meta_help::HelpItems::default();
             for x in xs.iter() {
diff --git a/src/params.rs b/src/params.rs
index 3707753..9f17f32 100644
--- a/src/params.rs
+++ b/src/params.rs
@@ -58,13 +58,10 @@
 //!
 #![doc = include_str!("docs/command.md")]
 //!
-use std::{ffi::OsString, marker::PhantomData};
+use std::{ffi::OsString, marker::PhantomData, str::FromStr};
 
-use super::{Args, Error, OptionParser, Parser};
-use crate::{args::Arg, from_os_str::FromOsStr, item::ShortLong, Item, Meta};
-
-#[cfg(doc)]
-use crate::from_os_str::FromUtf8;
+use super::{parse_os_str, Args, Error, OptionParser, Parser};
+use crate::{args::Arg, item::ShortLong, Item, Meta};
 
 /// A named thing used to create [`flag`](NamedArg::flag), [`switch`](NamedArg::switch) or
 /// [`argument`](NamedArg::argument)
@@ -353,8 +350,9 @@ impl NamedArg {
     /// literal can't start with `-` unless separated from the flag with `=`. For short flags value
     /// can follow immediately: `-fbar`.
     ///
-    /// When using combinatoring API you must specify the type with turbofish, for parsing types
-    /// that don't implement [`FromOsStr`] you can use [`FromUtf8`] helper tag.
+    /// When using combinatoring API you can specify the type with turbofish, for parsing types
+    /// that don't implement [`FromStr`] you can use consume a `String`/`OsString` first and parse
+    /// it by hands.
     /// ```rust
     /// # use bpaf::*;
     /// fn parse_arg() -> impl Parser<usize> {
@@ -365,7 +363,7 @@ impl NamedArg {
     #[must_use]
     pub fn argument<T>(self, metavar: &'static str) -> ParseArgument<T>
     where
-        T: FromOsStr,
+        T: FromStr + 'static,
     {
         build_argument(self, metavar)
     }
@@ -389,8 +387,9 @@ impl NamedArg {
 /// `cat hello world` and `cat world hello` would display contents of the same two files but in
 /// different order.
 ///
-/// When using combinatoring API you must specify the type with turbofish, for parsing types
-/// that don't implement [`FromOsStr`] you can use [`FromUtf8`] helper tag.
+/// When using combinatoring API you can specify the type with turbofish, for parsing types
+/// that don't implement [`FromStr`] you can use consume a `String`/`OsString` first and parse
+/// it by hands.
 /// ```no_run
 /// # use bpaf::*;
 /// fn parse_pos() -> impl Parser<usize> {
@@ -722,13 +721,14 @@ impl<T> ParseArgument<T> {
     }
 }
 
-impl<O, T> Parser<O> for ParseArgument<T>
+impl<T> Parser<T> for ParseArgument<T>
 where
-    T: FromOsStr<Out = O>,
+    T: FromStr + 'static,
+    <T as std::str::FromStr>::Err: std::fmt::Display,
 {
-    fn eval(&self, args: &mut Args) -> Result<O, Error> {
+    fn eval(&self, args: &mut Args) -> Result<T, Error> {
         let os = self.take_argument(args)?;
-        match T::from_os_str(os) {
+        match parse_os_str::<T>(os) {
             Ok(ok) => Ok(ok),
             Err(err) => Err(args.word_parse_error(&err)),
         }
@@ -901,13 +901,14 @@ fn parse_word(
     }
 }
 
-impl<O, T> Parser<O> for ParsePositional<T>
+impl<T> Parser<T> for ParsePositional<T>
 where
-    T: FromOsStr<Out = O>,
+    T: FromStr + 'static,
+    <T as std::str::FromStr>::Err: std::fmt::Display,
 {
-    fn eval(&self, args: &mut Args) -> Result<O, Error> {
+    fn eval(&self, args: &mut Args) -> Result<T, Error> {
         let os = parse_word(args, self.strict, self.metavar, &self.help)?;
-        match T::from_os_str(os) {
+        match parse_os_str::<T>(os) {
             Ok(ok) => Ok(ok),
             Err(err) => Err(args.word_parse_error(&err)),
         }
@@ -930,10 +931,12 @@ pub struct ParseAny<T> {
 ///
 /// `any` behaves similar to [`positional`] so you should be using it near the right most end of
 /// the consumer struct. Note, consuming "anything" also consumes `--help` unless restricted
-/// with `guard`
+/// with `guard`. It's better stick to `positional` unless you are trying to consume raw options
+/// to pass to some other process or do some special handling.
 ///
-/// When using combinatoring API you must specify the type with turbofish, for parsing types
-/// that don't implement [`FromOsStr`] you can use [`FromUtf8`] helper tag.
+/// When using combinatoring API you can specify the type with turbofish, for parsing types
+/// that don't implement [`FromStr`] you can use consume a `String`/`OsString` first and parse
+/// it by hands. For `any` you would usually consume it either as a `String` or `OsString`.
 /// ```no_run
 /// # use bpaf::*;
 /// # use std::ffi::OsString;
@@ -1007,13 +1010,14 @@ impl<T> ParseAny<T> {
     }
 }
 
-impl<T, O> Parser<O> for ParseAny<T>
+impl<T> Parser<T> for ParseAny<T>
 where
-    T: FromOsStr<Out = O>,
+    T: FromStr + 'static,
+    <T as std::str::FromStr>::Err: std::fmt::Display,
 {
-    fn eval(&self, args: &mut Args) -> Result<O, Error> {
+    fn eval(&self, args: &mut Args) -> Result<T, Error> {
         let os = self.next_os_string(args)?;
-        match T::from_os_str(os) {
+        match parse_os_str::<T>(os) {
             Ok(ok) => Ok(ok),
             Err(err) => Err(args.word_parse_error(&err)), // Error::Stderr(err)),
         }


Test patch:
diff --git a/tests/meta_youmean.rs b/tests/meta_youmean.rs
index f73dde6..9f7d9f9 100644
--- a/tests/meta_youmean.rs
+++ b/tests/meta_youmean.rs
@@ -24,3 +24,20 @@ fn ambiguity() {
         .unwrap_stderr();
     assert_eq!(r, "No such flag: `-b`, did you mean `-a`?");
 }
+
+#[test]
+fn short_cmd() {
+    let parser = long("alpha")
+        .req_flag(())
+        .to_options()
+        .command("beta")
+        .short('b')
+        .to_options();
+
+    let r = parser
+        .run_inner(Args::from(&["c"]))
+        .unwrap_err()
+        .unwrap_stderr();
+
+    assert_eq!(r, "No such command: `c`, did you mean `b`?");
+}