Skip to main content

openraft/config/
parser.rs

1//! Clap-based parsing for [`Config`].
2//!
3//! Gated behind `feature = "clap"` in [`super`]; functions and the
4//! [`Config::build`] method here are only compiled when that feature is on.
5
6use clap::Parser;
7
8use crate::Config;
9use crate::SnapshotPolicy;
10use crate::StepDownPolicy;
11use crate::config::error::ConfigError;
12use crate::errors::ErrorSource;
13use crate::impls::BoxedErrorSource;
14
15/// Parse number with unit such as 5.3 KB
16pub(super) fn parse_bytes_with_unit(src: &str) -> Result<u64, ConfigError> {
17    parse_size::parse_size(src).map_err(|e| ConfigError::InvalidNumber {
18        invalid: src.to_string(),
19        reason: e.to_string(),
20    })
21}
22
23pub(super) fn parse_snapshot_policy(src: &str) -> Result<SnapshotPolicy, ConfigError> {
24    if src == "never" {
25        return Ok(SnapshotPolicy::Never);
26    }
27
28    let elts = src.split(':').collect::<Vec<_>>();
29    if elts.len() != 2 {
30        return Err(ConfigError::InvalidSnapshotPolicy {
31            syntax: "never|since_last:<num>".to_string(),
32            invalid: src.to_string(),
33        });
34    }
35
36    if elts[0] != "since_last" {
37        return Err(ConfigError::InvalidSnapshotPolicy {
38            syntax: "never|since_last:<num>".to_string(),
39            invalid: src.to_string(),
40        });
41    }
42
43    let n_logs = elts[1].parse::<u64>().map_err(|e| ConfigError::InvalidNumber {
44        invalid: src.to_string(),
45        reason: e.to_string(),
46    })?;
47    Ok(SnapshotPolicy::LogsSinceLast(n_logs))
48}
49
50/// Parse a step-down policy: a "never" literal (`never`, `no`, `none`, `off` or `false`,
51/// case-insensitive), or the number of milliseconds to wait.
52pub(super) fn parse_step_down_policy(src: &str) -> Result<StepDownPolicy, ConfigError> {
53    const NEVER_LITERALS: &[&str] = &["never", "no", "none", "off", "false"];
54
55    if NEVER_LITERALS.iter().any(|x| src.eq_ignore_ascii_case(x)) {
56        return Ok(StepDownPolicy::Never);
57    }
58
59    let ms = src.parse::<u64>().map_err(|e| ConfigError::InvalidNumber {
60        invalid: src.to_string(),
61        reason: e.to_string(),
62    })?;
63    Ok(StepDownPolicy::After(ms))
64}
65
66impl Config {
67    /// Build a `Config` instance from a series of command line arguments.
68    ///
69    /// The first element in `args` must be the application name.
70    ///
71    /// Only available when the `clap` feature is enabled (default).
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use openraft::Config;
77    ///
78    /// let config = Config::build(&[
79    ///     "myapp",
80    ///     "--election-timeout-min", "300",
81    ///     "--election-timeout-max", "500",
82    /// ])?;
83    /// # Ok::<(), openraft::ConfigError>(())
84    /// ```
85    pub fn build(args: &[&str]) -> Result<Config, ConfigError> {
86        let config = <Self as Parser>::try_parse_from(args).map_err(|e| ConfigError::ParseError {
87            source: BoxedErrorSource::from_error(&e),
88            args: args.iter().map(|x| x.to_string()).collect(),
89        })?;
90        config.validate()
91    }
92}