option_parser: Add documentation strings

Autogenerated with Claude Opus 4.6 and reviewed with human eyes.

Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-04-14 07:49:46 +01:00
parent 791889cefd
commit d212255073

View File

@@ -3,6 +3,29 @@
// SPDX-License-Identifier: Apache-2.0
//
//! A parser for comma-separated `key=value` option strings.
//!
//! This crate provides [`OptionParser`], which parses strings of the form
//! `"key1=value1,key2=value2,..."` into a set of named options that can then
//! be retrieved and converted to various types.
//!
//! Values may be quoted with `"` to embed commas and other special characters,
//! and brackets `[` `]` are tracked so that list-valued options like
//! `topology=[1,2,3]` are not split at inner commas.
//!
//! # Example
//!
//! ```
//! use option_parser::OptionParser;
//!
//! let mut parser = OptionParser::new();
//! parser.add("size").add("mergeable");
//! parser.parse("size=128M,mergeable=on").unwrap();
//!
//! assert_eq!(parser.get("size"), Some("128M".to_owned()));
//! assert_eq!(parser.get("mergeable"), Some("on".to_owned()));
//! ```
use std::collections::HashMap;
use std::fmt::{Display, Write};
use std::num::ParseIntError;
@@ -27,6 +50,12 @@ mod private_trait {
}
use private_trait::Parseable;
/// A parser for comma-separated `key=value` option strings.
///
/// Options must be registered with [`add`](Self::add) or
/// [`add_valueless`](Self::add_valueless) before parsing. After calling
/// [`parse`](Self::parse), values can be retrieved with [`get`](Self::get)
/// or converted to a specific type with [`convert`](Self::convert).
#[derive(Default)]
pub struct OptionParser {
options: HashMap<String, OptionParserValue>,
@@ -37,14 +66,19 @@ struct OptionParserValue {
requires_value: bool,
}
/// Errors returned when parsing or converting options.
#[derive(Debug, Error)]
pub enum OptionParserError {
/// An option name was not previously registered with [`OptionParser::add`].
#[error("unknown option: {0}")]
UnknownOption(String),
/// The input string has invalid syntax (unbalanced quotes/brackets, missing `=`).
#[error("unknown option: {0}")]
InvalidSyntax(String),
/// A value could not be converted to the requested type.
#[error("unable to convert {1} for {0}")]
Conversion(String /* field */, String /* value */),
/// A value was syntactically valid but semantically wrong.
#[error("invalid value: {0}")]
InvalidValue(String),
}
@@ -87,6 +121,7 @@ fn split_commas(s: &str) -> OptionParserResult<Vec<String>> {
}
impl OptionParser {
/// Creates an empty `OptionParser` with no registered options.
pub fn new() -> Self {
Self {
options: HashMap::new(),
@@ -122,14 +157,30 @@ impl OptionParser {
Ok(())
}
/// Parses a comma-separated `key=value` string, updating registered options.
///
/// Returns an error if the input contains an unknown option name, has
/// unbalanced quotes or brackets, or a value-requiring option lacks `=`.
pub fn parse(&mut self, input: &str) -> OptionParserResult<()> {
self.parse_inner(input, false)
}
/// Like [`parse`](Self::parse), but silently ignores unknown option names.
///
/// This is useful when multiple parsers share the same input string and
/// each only cares about a subset of the options.
pub fn parse_subset(&mut self, input: &str) -> OptionParserResult<()> {
self.parse_inner(input, true)
}
/// Registers a named option that requires a value (i.e. `key=value`).
///
/// Option names must not contain `"`, `[`, `]`, `=`, or `,`.
/// Returns `&mut Self` for chaining.
///
/// # Panics
///
/// Panics if the option name contains a forbidden character.
pub fn add(&mut self, option: &str) -> &mut Self {
// Check that option=value has balanced
// quotes and brackets iff value does.
@@ -148,6 +199,9 @@ impl OptionParser {
self
}
/// Registers multiple value-requiring options at once.
///
/// Equivalent to calling [`add`](Self::add) for each element in the slice.
pub fn add_all(&mut self, options: &[&str]) -> &mut Self {
for option in options {
self.add(option);
@@ -156,6 +210,10 @@ impl OptionParser {
self
}
/// Registers a flag-style option that does not take a value.
///
/// When this option appears in the input string (without `=`), it is
/// marked as set. Use [`is_set`](Self::is_set) to query it.
pub fn add_valueless(&mut self, option: &str) -> &mut Self {
self.options.insert(
option.to_owned(),
@@ -168,6 +226,10 @@ impl OptionParser {
self
}
/// Returns the raw string value of an option, or `None` if the option was
/// not set or if its value is an empty string (e.g. `key=`).
///
/// Surrounding double-quotes in the value are removed.
pub fn get(&self, option: &str) -> Option<String> {
self.options
.get(option)
@@ -181,6 +243,9 @@ impl OptionParser {
})
}
/// Returns `true` if the option was present in the parsed input.
///
/// This works for both value-requiring and valueless options.
pub fn is_set(&self, option: &str) -> bool {
self.options
.get(option)
@@ -188,6 +253,14 @@ impl OptionParser {
.is_some()
}
/// Retrieves and converts an option value to type `T`.
///
/// Returns `Ok(None)` if the option was not set or its value is empty.
/// Returns `Err` if the value cannot be converted to `T`.
///
/// `T` can be any type that implements `FromStr` (e.g. `u32`, `String`),
/// or one of this crate's types such as [`Toggle`], [`IntegerList`],
/// [`Tuple`], or [`StringList`].
pub fn convert<T: Parseable>(&self, option: &str) -> OptionParserResult<Option<T>> {
match self.options.get(option).and_then(|v| v.value.as_ref()) {
None => Ok(None),
@@ -204,6 +277,9 @@ impl OptionParser {
}
}
/// A boolean-like value that accepts `"on"`, `"true"`, `"off"`, `"false"`, or `""`.
///
/// An empty string is treated as `false`.
pub struct Toggle(pub bool);
#[derive(Error, Debug)]
@@ -227,6 +303,10 @@ impl Parseable for Toggle {
}
}
/// A byte size parsed from a human-readable string with optional `K`, `M`, or `G` suffix.
///
/// The suffix is binary (1K = 1024, 1M = 1048576, 1G = 1073741824).
/// A bare integer is treated as bytes.
pub struct ByteSized(pub u64);
#[derive(Error, Debug)]
@@ -259,6 +339,9 @@ impl FromStr for ByteSized {
}
}
/// A list of integers parsed from a bracket-enclosed, comma-separated string.
///
/// Ranges are supported with `-`: `"[0,2-4,6]"` produces `[0, 2, 3, 4, 6]`.
pub struct IntegerList(pub Vec<u64>);
impl Display for IntegerList {
@@ -324,7 +407,11 @@ impl Parseable for IntegerList {
}
}
/// Types that can appear as the second element of a [`Tuple`] pair.
///
/// Implemented for `u64`, `Vec<u8>`, `Vec<u64>`, and `Vec<usize>`.
pub trait TupleValue {
/// Parses the value portion of a `key@value` tuple element.
fn parse_value(input: &str) -> Result<Self, TupleError>
where
Self: Sized;
@@ -366,6 +453,10 @@ impl TupleValue for Vec<usize> {
}
}
/// A list of `key@value` pairs parsed from a bracket-enclosed string.
///
/// The format is `[key1@value1,key2@value2,...]` where `@` separates each
/// pair's elements. `S` is the key type and `T` is the value type.
#[derive(PartialEq, Eq, Debug)]
pub struct Tuple<S, T>(pub Vec<(S, T)>);
@@ -421,6 +512,9 @@ impl<S: Parseable, T: TupleValue> Parseable for Tuple<S, T> {
}
}
/// A list of strings parsed from a bracket-enclosed, comma-separated string.
///
/// The format is `[str1,str2,...]`. Brackets are optional.
#[derive(Default)]
pub struct StringList(pub Vec<String>);