#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum ArgValue {
Text(String),
Integer(i64),
Number(f64),
}
impl From<String> for ArgValue {
fn from(value: String) -> Self {
Self::Text(value)
}
}
impl From<&str> for ArgValue {
fn from(value: &str) -> Self {
Self::Text(value.to_owned())
}
}
impl From<i64> for ArgValue {
fn from(value: i64) -> Self {
Self::Integer(value)
}
}
impl From<i32> for ArgValue {
fn from(value: i32) -> Self {
Self::Integer(i64::from(value))
}
}
impl From<u32> for ArgValue {
fn from(value: u32) -> Self {
Self::Integer(i64::from(value))
}
}
impl From<usize> for ArgValue {
fn from(value: usize) -> Self {
Self::Integer(i64::try_from(value).unwrap_or(i64::MAX))
}
}
impl From<f64> for ArgValue {
fn from(value: f64) -> Self {
Self::Number(value)
}
}
#[derive(Debug, Clone, Default, PartialEq)]
#[non_exhaustive]
pub struct TranslationArgs {
entries: Vec<(String, ArgValue)>,
}
impl TranslationArgs {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with(mut self, name: impl Into<String>, value: impl Into<ArgValue>) -> Self {
let name = name.into();
let value = value.into();
match self
.entries
.iter_mut()
.find(|(existing, _)| *existing == name)
{
Some(entry) => entry.1 = value,
None => self.entries.push((name, value)),
}
self
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &ArgValue)> {
self.entries
.iter()
.map(|(name, value)| (name.as_str(), value))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn arguments_keep_their_insertion_order() {
let args = TranslationArgs::new()
.with("b", 1)
.with("a", 2)
.with("c", "three");
let names: Vec<&str> = args.iter().map(|(name, _)| name).collect();
assert_eq!(names, ["b", "a", "c"]);
assert_eq!(args.len(), 3);
assert!(!args.is_empty());
}
#[test]
fn setting_a_name_twice_keeps_the_later_value() {
let args = TranslationArgs::new().with("n", 1).with("n", 2);
assert_eq!(args.len(), 1);
assert_eq!(args.iter().next().unwrap().1, &ArgValue::Integer(2));
}
#[test]
fn an_integer_and_a_float_are_different_values() {
assert_eq!(ArgValue::from(1_i64), ArgValue::Integer(1));
assert_eq!(ArgValue::from(1.0_f64), ArgValue::Number(1.0));
assert_ne!(ArgValue::from(1_i64), ArgValue::from(1.0_f64));
}
#[test]
fn a_usize_too_large_for_an_i64_saturates() {
assert_eq!(ArgValue::from(7_usize), ArgValue::Integer(7));
assert_eq!(ArgValue::from(usize::MAX), ArgValue::Integer(i64::MAX));
}
#[test]
fn an_empty_set_is_empty() {
assert!(TranslationArgs::new().is_empty());
assert_eq!(TranslationArgs::new().len(), 0);
}
}