chrono::offset::FixedOffset cannot be used by Crate clap due to it does not implement the FromStr trait

375 Views Asked by At

I'm writing code for a command line tool in Rust. I'm using Crate clap to get and parse command line arguments, I need to get three arguments: --date, --time and --timezone.

For --date and --time I'm using types chrono::NaiveDate and chrono::NaiveTime respectively and it works fine.

src/main.rs

use clap::Parser;
use chrono::prelude::*;


// CLI struct
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] // Read from `Cargo.toml`
pub struct Cli {
    /// Select a date in format YYYY-MM-DD.
    #[arg(short, long, value_name = "DATE")]
    date: Option<NaiveDate>,

    /// Clocking time in format HH:MM:SS
    #[arg(short, long, value_name = "TIME")]
    time: Option<NaiveTime>,
}

fn main() {
    let _cli: Cli = Cli::parse();
    println!("{:#?}", _cli);
}

Output

$ cargo run -- --date 2021-05-13 --time 08:34:23
    Finished dev [unoptimized + debuginfo] target(s) in 2.28s
     Running `target/debug/tool --date 2021-05-13 --time '08:34:23'`
Cli {
    date: Some(
        2021-05-13,
    ),
    time: Some(
        08:34:23,
    ),
}

but when I want add --timezone argument and use it as type chrono::FixedOffset, like this:

[...]

pub struct Cli {
    /// Select a date in format YYYY-MM-DD.
    #[arg(short, long, value_name = "DATE")]
    date: Option<NaiveDate>,

    /// Clocking time in format HH:MM:SS
    #[arg(short, long, value_name = "TIME")]
    time: Option<NaiveTime>,

    /// Define local TimeZone
    #[arg(long, value_name = "TIMEZONE")]
    timezone: Option<FixedOffset>,
}

[...]

Rust compiler shows this error:

$ cargo run -- --date 2021-05-13 --time 08:34:23
   Compiling tool v0.1.0 (~/rust/tool)
error[E0599]: the method `value_parser` exists for reference `&&&&&&_AutoValueParser<chrono::FixedOffset>`, but its trait bounds were not satisfied
    --> src/main.rs:19:5
     |
19   |     /// Define local TimeZone
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `&&&&&&_AutoValueParser<chrono::FixedOffset>` due to unsatisfied trait bounds
     |
    ::: ~/.cargo/registry/src/github.com-1ecc6299db9ec823/clap-4.0.18/src/builder/value_parser.rs:2171:1
     |
2171 | pub struct _AutoValueParser<T>(std::marker::PhantomData<T>);
     | ------------------------------ doesn't satisfy `_: clap::builder::via_prelude::_ValueParserViaParse`
     |
    ::: ~/.cargo/registry/src/github.com-1ecc6299db9ec823/chrono-0.4.22/src/offset/fixed.rs:27:1
     |
27   | pub struct FixedOffset {
     | ----------------------
     | |
     | doesn't satisfy `chrono::FixedOffset: From<&'s std::ffi::OsStr>`
     | doesn't satisfy `chrono::FixedOffset: From<&'s str>`
     | doesn't satisfy `chrono::FixedOffset: From<OsString>`
     | doesn't satisfy `chrono::FixedOffset: From<std::string::String>`
     | doesn't satisfy `chrono::FixedOffset: FromStr`
     | doesn't satisfy `chrono::FixedOffset: ValueEnum`
     | doesn't satisfy `chrono::FixedOffset: ValueParserFactory`
     |
     = note: the following trait bounds were not satisfied:
             `chrono::FixedOffset: ValueEnum`
             which is required by `&&&&&_AutoValueParser<chrono::FixedOffset>: clap::builder::via_prelude::_ValueParserViaValueEnum`
             `chrono::FixedOffset: ValueParserFactory`
             which is required by `&&&&&&_AutoValueParser<chrono::FixedOffset>: clap::builder::via_prelude::_ValueParserViaFactory`
             `chrono::FixedOffset: From<OsString>`
             which is required by `&&&&_AutoValueParser<chrono::FixedOffset>: clap::builder::via_prelude::_ValueParserViaFromOsString`
             `chrono::FixedOffset: From<&'s std::ffi::OsStr>`
             which is required by `&&&_AutoValueParser<chrono::FixedOffset>: clap::builder::via_prelude::_ValueParserViaFromOsStr`
             `chrono::FixedOffset: From<std::string::String>`
             which is required by `&&_AutoValueParser<chrono::FixedOffset>: clap::builder::via_prelude::_ValueParserViaFromString`
             `chrono::FixedOffset: From<&'s str>`
             which is required by `&_AutoValueParser<chrono::FixedOffset>: clap::builder::via_prelude::_ValueParserViaFromStr`
             `chrono::FixedOffset: FromStr`
             which is required by `_AutoValueParser<chrono::FixedOffset>: clap::builder::via_prelude::_ValueParserViaParse`
     = note: this error originates in the macro `clap::value_parser` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0599`.
error: could not compile `tool` due to 2 previous errors

Should I use another type for timezone variable?
Should I implement FromStr trait to FixedOffset struct?

I could use timezone variable as Type String, and it works, but I want to use a specific type for TimeZone or Offset, for instance, to save +01:00 value. I will have to manage this param isolated.

I don't want to use chrono::DateTime struct because I will use default values for these arguments, usually, the user only will define --time parameter and I won't force him to write all DateTime.

Thank you

1

There are 1 best solutions below

0
On

You can't implement FromStr for FixedOffset because you wrote neither the FromStr trait nor the FixedOffset struct and because of the orphan rule. However you will indeed need to write a function that converts a string into a FixedOffset and pass it to clap using the value_parser attribute:

use clap::Parser;
use chrono::prelude::*;


// CLI struct
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] // Read from `Cargo.toml`
pub struct Cli {
    /// Select a date in format YYYY-MM-DD.
    #[arg(short, long, value_name = "DATE")]
    date: Option<NaiveDate>,

    /// Clocking time in format HH:MM:SS
    #[arg(short, long, value_name = "TIME")]
    time: Option<NaiveTime>,

    /// Define local TimeZone
    #[arg(long, value_name = "TIMEZONE", value_parser = parse_timezone)]
    timezone: Option<FixedOffset>,
}

fn parse_timezone (tz: &str) -> Result<FixedOffset, &'static str> {
    /// TODO: Parse the string timezone into a `FixedOffset`
    Err ("Not implemented")
}

fn main() {
    let _cli: Cli = Cli::parse();
    println!("{:#?}", _cli);
}

Playground