use crate::Other;
pub enum NumberConstraint {
None,
Positive,
NonNegative,
Range(f64, f64),
}
pub enum BoolType {
TrueFalse,
YesNo,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Bool {
Ok(bool),
Other(Other),
}
impl Default for Bool {
fn default() -> Self {
Bool::Ok(false)
}
}
impl Bool {
pub fn parse(s: &str, bool_type: BoolType) -> Self {
match bool_type {
BoolType::TrueFalse => match s {
"true" => Bool::Ok(true),
"false" => Bool::Ok(false),
_ => Bool::Other((s.to_string(), "should be \"true\" or \"false\"".to_string())),
},
BoolType::YesNo => match s {
"yes" => Bool::Ok(true),
"no" => Bool::Ok(false),
_ => Bool::Other((s.to_string(), "should be \"yes\" or \"no\"".to_string())),
},
}
}
}
impl std::str::FromStr for Bool {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.parse::<bool>() {
Ok(x) => Ok(Self::Ok(x)),
Err(e) => Ok(Self::Other((s.to_string(), e.to_string()))),
}
}
}
impl std::fmt::Display for Bool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ok(t) => write!(f, "{t}"),
Self::Other((s, _)) => write!(f, "{s}"),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Integer {
Ok(i64),
Other(Other),
}
impl Integer {
pub fn parse(s: &str, constraint: NumberConstraint) -> Self {
match s.parse::<i64>() {
Ok(x) => match constraint {
NumberConstraint::None => Self::Ok(x),
NumberConstraint::Positive => {
if x > 0 {
Self::Ok(x)
} else {
Self::Other((s.to_string(), "should be positive".to_string()))
}
}
NumberConstraint::NonNegative => {
if x >= 0 {
Self::Ok(x)
} else {
Self::Other((s.to_string(), "should be non-negative".to_string()))
}
}
NumberConstraint::Range(min, max) => {
if x as f64 >= min && x as f64 <= max {
Self::Ok(x)
} else {
Self::Other((s.to_string(), format!("should be in range [{min}, {max}]")))
}
}
},
Err(_) => Self::Other((s.to_string(), "should be an integer".to_string())),
}
}
}
impl std::fmt::Display for Integer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ok(t) => write!(f, "{t}"),
Self::Other((s, _)) => write!(f, "{s}"),
}
}
}
#[derive(Debug, PartialEq)]
pub enum Float {
Ok(f64),
Other(Other),
}
impl Float {
pub fn parse(s: &str, constraint: NumberConstraint) -> Self {
match s.parse::<f64>() {
Ok(x) => match constraint {
NumberConstraint::None => Self::Ok(x),
NumberConstraint::Positive => {
if x > 0.0 {
Self::Ok(x)
} else {
Self::Other((s.to_string(), "should be positive".to_string()))
}
}
NumberConstraint::NonNegative => {
if x >= 0.0 {
Self::Ok(x)
} else {
Self::Other((s.to_string(), "should be non-negative".to_string()))
}
}
NumberConstraint::Range(min, max) => {
if x >= min && x <= max {
Self::Ok(x)
} else {
Self::Other((s.to_string(), format!("should be in range [{min}, {max}]")))
}
}
},
Err(_) => Self::Other((
s.to_string(),
"should be a floating-point number".to_string(),
)),
}
}
}
impl std::fmt::Display for Float {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ok(t) => write!(f, "{t}"),
Self::Other((s, _)) => write!(f, "{s}"),
}
}
}
#[derive(Debug, PartialEq)]
pub enum Number {
Integer(i64),
Float(f64),
Other(Other),
}
impl Number {
pub fn parse(s: &str, constraint: NumberConstraint) -> Self {
match s.parse::<i64>() {
Ok(x) => match constraint {
NumberConstraint::None => Self::Integer(x),
NumberConstraint::Positive => {
if x > 0 {
Self::Integer(x)
} else {
Self::Other((s.to_string(), "should be positive".to_string()))
}
}
NumberConstraint::NonNegative => {
if x >= 0 {
Self::Integer(x)
} else {
Self::Other((s.to_string(), "should be non-negative".to_string()))
}
}
NumberConstraint::Range(min, max) => {
if x as f64 >= min && x as f64 <= max {
Self::Integer(x)
} else {
Self::Other((s.to_string(), format!("should be in range [{min}, {max}]")))
}
}
},
Err(_) => match s.parse::<f64>() {
Ok(x) => match constraint {
NumberConstraint::None => Self::Float(x),
NumberConstraint::Positive => {
if x > 0.0 {
Self::Float(x)
} else {
Self::Other((s.to_string(), "should be positive".to_string()))
}
}
NumberConstraint::NonNegative => {
if x >= 0.0 {
Self::Float(x)
} else {
Self::Other((s.to_string(), "should be non-negative".to_string()))
}
}
NumberConstraint::Range(min, max) => {
if x >= min && x <= max {
Self::Float(x)
} else {
Self::Other((
s.to_string(),
format!("should be in range [{min}, {max}]"),
))
}
}
},
Err(_) => Self::Other((s.to_string(), "should be a number".to_string())),
},
}
}
}
impl std::fmt::Display for Number {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer(t) => write!(f, "{t}"),
Self::Float(t) => write!(f, "{t}"),
Self::Other((s, _)) => write!(f, "{s}"),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Duration {
Duration(chrono::Duration),
Other(Other),
}
impl Duration {
pub fn parse_from_int_string(s: &str) -> Self {
match s.parse::<u64>() {
Ok(x) => Self::Duration(chrono::Duration::seconds(x as i64)),
Err(_) => Self::Other((
s.to_string(),
"should be a non-negative integer".to_string(),
)),
}
}
pub fn parse_from_float_string(s: &str) -> Self {
match s.parse::<f64>() {
Ok(x) => {
let x = (x * 1_000_000_000.0).round() as i64;
Self::Duration(chrono::Duration::nanoseconds(x))
}
Err(_) => Self::Other((
s.to_string(),
"should be a non-negative floating-point number".to_string(),
)),
}
}
}