use crate::time::{Multiplier, TimeUnit, DEFAULT_TIME_UNIT};
pub(crate) const DEFAULT_CONFIG: Config = Config::new();
pub type Delimiter = fn(u8) -> bool;
pub trait NumbersLike {
fn get(&self, input: &str) -> Option<Multiplier>;
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[allow(clippy::struct_excessive_bools)]
#[non_exhaustive]
pub struct Config<'a> {
pub default_unit: TimeUnit,
pub default_multiplier: Multiplier,
pub disable_exponent: bool,
pub disable_fraction: bool,
pub disable_infinity: bool,
pub number_is_optional: bool,
pub allow_multiple: bool,
pub conjunctions: Option<&'a [&'a str]>,
pub allow_negative: bool,
pub inner_delimiter: Delimiter,
pub outer_delimiter: Delimiter,
pub allow_ago: bool,
pub allow_sign_delimiter: bool,
pub allow_time_unit_delimiter: bool,
}
impl<'a> Default for Config<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Config<'a> {
pub const fn new() -> Self {
Self {
allow_time_unit_delimiter: false,
default_unit: DEFAULT_TIME_UNIT,
default_multiplier: Multiplier(1, 0),
disable_exponent: false,
disable_fraction: false,
number_is_optional: false,
disable_infinity: false,
allow_multiple: false,
conjunctions: None,
allow_negative: false,
allow_ago: false,
allow_sign_delimiter: false,
inner_delimiter: |byte| byte.is_ascii_whitespace(),
outer_delimiter: |byte| byte.is_ascii_whitespace(),
}
}
pub const fn builder() -> ConfigBuilder<'a> {
ConfigBuilder::new()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct ConfigBuilder<'a> {
config: Config<'a>,
}
impl<'a> ConfigBuilder<'a> {
pub const fn new() -> Self {
Self {
config: Config::new(),
}
}
pub const fn build(self) -> Config<'a> {
self.config
}
pub const fn allow_time_unit_delimiter(mut self) -> Self {
self.config.allow_time_unit_delimiter = true;
self
}
pub const fn default_unit(mut self, time_unit: TimeUnit) -> Self {
self.config.default_unit = time_unit;
self
}
pub const fn disable_exponent(mut self) -> Self {
self.config.disable_exponent = true;
self
}
pub const fn disable_fraction(mut self) -> Self {
self.config.disable_fraction = true;
self
}
pub const fn disable_infinity(mut self) -> Self {
self.config.disable_infinity = true;
self
}
pub const fn number_is_optional(mut self) -> Self {
self.config.number_is_optional = true;
self
}
pub const fn allow_negative(mut self) -> Self {
self.config.allow_negative = true;
self
}
pub const fn parse_multiple(mut self, conjunctions: Option<&'a [&'a str]>) -> Self {
self.config.allow_multiple = true;
self.config.conjunctions = conjunctions;
self
}
pub const fn allow_ago(mut self) -> Self {
self.config.allow_ago = true;
self.config.allow_negative = true;
self
}
pub const fn allow_sign_delimiter(mut self) -> Self {
self.config.allow_sign_delimiter = true;
self
}
pub const fn inner_delimiter(mut self, delimiter: Delimiter) -> Self {
self.config.inner_delimiter = delimiter;
self
}
pub const fn outer_delimiter(mut self, delimiter: Delimiter) -> Self {
self.config.outer_delimiter = delimiter;
self
}
}
#[cfg(test)]
mod tests {
use rstest::{fixture, rstest};
use super::*;
#[fixture]
pub fn test_time_unit() -> TimeUnit {
TimeUnit::MilliSecond
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_default_for_config() {
assert_eq!(Config::default(), Config::new());
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_default_for_config_builder() {
assert_eq!(ConfigBuilder::new().build(), Config::new());
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_method_builder() {
assert_eq!(Config::builder().build(), Config::new());
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_allow_delimiter() {
let config = ConfigBuilder::new().allow_time_unit_delimiter().build();
let mut expected = Config::new();
expected.allow_time_unit_delimiter = true;
assert_eq!(config, expected);
}
#[rstest]
#[cfg_attr(miri, ignore)]
fn test_config_builder_default_unit(test_time_unit: TimeUnit) {
let config = ConfigBuilder::new().default_unit(test_time_unit).build();
let mut expected = Config::new();
expected.default_unit = test_time_unit;
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_disable_exponent() {
let config = ConfigBuilder::new().disable_exponent().build();
let mut expected = Config::new();
expected.disable_exponent = true;
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_disable_fraction() {
let config = ConfigBuilder::new().disable_fraction().build();
let mut expected = Config::new();
expected.disable_fraction = true;
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_number_is_optional() {
let config = ConfigBuilder::new().number_is_optional().build();
let mut expected = Config::new();
expected.number_is_optional = true;
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_disable_infinity() {
let config = ConfigBuilder::new().disable_infinity().build();
let mut expected = Config::new();
expected.disable_infinity = true;
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_allow_negative() {
let config = ConfigBuilder::new().allow_negative().build();
let mut expected = Config::new();
expected.allow_negative = true;
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_parse_multiple_when_no_conjunctions() {
let config = ConfigBuilder::new().parse_multiple(None).build();
let mut expected = Config::new();
expected.allow_multiple = true;
expected.conjunctions = None;
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_parse_multiple_when_conjunctions() {
let conjunctions = &["and", ","];
let config = ConfigBuilder::new()
.parse_multiple(Some(conjunctions))
.build();
let mut expected = Config::new();
expected.allow_multiple = true;
expected.conjunctions = Some(conjunctions);
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_allow_ago() {
let config = ConfigBuilder::new().allow_ago().build();
let mut expected = Config::new();
expected.allow_ago = true;
expected.allow_negative = true;
assert_eq!(config, expected);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_config_builder_allow_sign_delimiter() {
let config = ConfigBuilder::new().allow_sign_delimiter().build();
let mut expected = Config::new();
expected.allow_sign_delimiter = true;
assert_eq!(config, expected);
}
}