Clap stuct fromStr - how to throw an Error?

I've got this Clap CLI definition:


#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
    #[arg(short, long, default_value_t = String::from("stdout"))]
    /// host:port, or alternatively "stdout" which will deactivate the udp sender and just print to stdout.
    udp_target: String,

    #[arg(short = 'n', long)]
    hostname: String,

    #[arg(short = 'z', long)]
    skip_zone: Vec<u8>,

    #[arg(short = 's', long)]
    skip_sysinfo_zone: Vec<String>,

    #[arg(short, long)]
    debug: bool,

    #[arg(short = 'S', long)]
    /// specify this to omit the zone tag. If you have more then one zone, the data will be mixed / scrambled into a single metric!
    single: bool,
    
    #[arg(short, long)]
    /// format: zone;devisor?;regex_unpacker?;cmd;arg1?;..;argN? - ?=optional, use ";;" to skip in-between parts.
    external: Vec<External>,

    #[arg(short = 'D', long, default_value_t = 1000.0)]
    zone_devisor: f32,

    #[arg(short, long, default_value_t = 80)]
    /// how often should variables be sent when their value doesn't change? in seconds.
    minimum_frequency: u16,
}

#[derive(Debug, Clone)]
struct External {
    zone: String,
    devisor: f32,
    regex_unpack: String,
    cmd: String,
    args: Vec<String>,
}

// https://doc.rust-lang.org/std/str/trait.FromStr.html
impl FromStr for External {
    type Err = std::string::ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split(";").collect();
        
        if parts.len() < 4 {
            panic!("An external command definition requires at least 4 parts, see definition in help output.");
        }

        Ok(External {
            zone: String::from(parts[0]),
            devisor: parts[1].parse().unwrap_or(1.0),
            regex_unpack: String::from(parts[2]),
            cmd: String::from(parts[3]),
            args: parts[4..].iter().map(|x| String::from(*x)).collect(),
        })
    }
}

And in the FromStr implementation I'd like to throw an Error (that contains a &str or String) instead of using the panic!(..) macro.
But nothing I've tried so far worked. Help please..

Replace the associated Err type with &'static str and replace the panic with return Err("message");.

Thanks for that hint!
After interacting with the compiler a bit I now reached this:

impl FromStr for External {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split(";").collect();
        
        if parts.len() < 4 {
            Err("An external command definition requires at least 4 parts, see definition in help output.")
        }
        else {
            Ok(External {
                zone: String::from(parts[0]),
                devisor: parts[1].parse().unwrap_or(1.0),
                regex_unpack: String::from(parts[2]),
                cmd: String::from(parts[3]),
                args: parts[4..].iter().map(|x| String::from(*x)).collect(),
            })
        }
    }
}

So:

  • no space in &'static
  • no semicolon after Err(..) (to make it the return value instead of a "normal" command.)

I didn't think "anything" like a simple &str could be used as the Err type, that's really nice actually!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.