use std::borrow::Cow;
use serde::{Deserialize, Serialize};
use crate::error::Error;
use crate::result::Result;
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Operand {
Integer(u64),
IntegerList(Vec<u64>),
Text(String),
TextList(Vec<String>),
}
impl Operand {
pub const fn new() -> Self {
Self::Integer(0)
}
pub(crate) const fn variant_name(&self) -> &'static str {
match self {
Self::Integer(_) => "Operand::Integer",
Self::IntegerList(_) => "Operand::IntegerList",
Self::Text(_) => "Operand::Text",
Self::TextList(_) => "Operand::TextList",
}
}
pub fn as_integer(&self) -> Result<u64> {
match self {
Self::Integer(v) => Ok(*v),
o => Err(Error::invalid_field(
"operand",
o.variant_name(),
self.variant_name(),
)),
}
}
pub fn integer_list<'c, C: Into<Cow<'c, [u64]>>>(val: C) -> Self {
Self::IntegerList(C::into(val).into())
}
pub fn as_integer_list(&self) -> Result<&[u64]> {
match self {
Self::IntegerList(v) => Ok(v.as_slice()),
o => Err(Error::invalid_field(
"operand",
o.variant_name(),
self.variant_name(),
)),
}
}
pub fn text<'c, C: Into<Cow<'c, str>>>(val: C) -> Self {
Self::Text(C::into(val).into())
}
pub fn as_text(&self) -> Result<&str> {
match self {
Self::Text(v) => Ok(v.as_str()),
o => Err(Error::invalid_field(
"operand",
o.variant_name(),
self.variant_name(),
)),
}
}
pub fn text_list<'c, C: Into<Cow<'c, [String]>>>(val: C) -> Self {
Self::TextList(C::into(val).into())
}
pub fn as_text_list(&self) -> Result<&[String]> {
match self {
Self::TextList(v) => Ok(v.as_slice()),
o => Err(Error::invalid_field(
"operand",
o.variant_name(),
self.variant_name(),
)),
}
}
}
impl Default for Operand {
fn default() -> Self {
Self::new()
}
}
impl From<u64> for Operand {
fn from(val: u64) -> Self {
Self::Integer(val)
}
}
impl From<Vec<u64>> for Operand {
fn from(val: Vec<u64>) -> Self {
Self::IntegerList(val)
}
}
impl From<&[u64]> for Operand {
fn from(val: &[u64]) -> Self {
Self::IntegerList(val.into())
}
}
impl<const N: usize> From<&[u64; N]> for Operand {
fn from(val: &[u64; N]) -> Self {
Self::IntegerList(val.into())
}
}
impl<const N: usize> From<[u64; N]> for Operand {
fn from(val: [u64; N]) -> Self {
Self::IntegerList(val.into_iter().collect())
}
}
impl From<&str> for Operand {
fn from(val: &str) -> Self {
Self::Text(val.into())
}
}
impl From<String> for Operand {
fn from(val: String) -> Self {
Self::Text(val)
}
}
impl From<Vec<String>> for Operand {
fn from(val: Vec<String>) -> Self {
Self::TextList(val)
}
}
impl From<&[String]> for Operand {
fn from(val: &[String]) -> Self {
Self::TextList(val.into())
}
}
impl From<&[&str]> for Operand {
fn from(val: &[&str]) -> Self {
Self::TextList(val.iter().map(|&s| String::from(s)).collect())
}
}
impl<const N: usize> From<&[String; N]> for Operand {
fn from(val: &[String; N]) -> Self {
Self::TextList(val.into())
}
}
impl<const N: usize> From<&[&str; N]> for Operand {
fn from(val: &[&str; N]) -> Self {
Self::TextList(val.iter().map(|&s| String::from(s)).collect())
}
}
impl<const N: usize> From<[String; N]> for Operand {
fn from(val: [String; N]) -> Self {
Self::TextList(val.into())
}
}
impl<const N: usize> From<[&str; N]> for Operand {
fn from(val: [&str; N]) -> Self {
Self::TextList(val.into_iter().map(String::from).collect())
}
}
impl std::fmt::Display for Operand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
serde_json::to_string(self)
.map_err(|_| std::fmt::Error)
.and_then(|s| write!(f, "{s}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_operand() {
(0..=u64::BITS)
.map(|r| ((1u128 << r) - 1) as u64)
.enumerate()
.for_each(|(i, int)| {
let list_len = i + 1;
let int_list = vec![int; list_len];
let text = format!("{int}");
let text_list = vec![text.clone(); list_len];
let op_int = Operand::Integer(int);
let op_int_list = Operand::IntegerList(int_list.clone());
let op_text = Operand::Text(text.clone());
let op_text_list = Operand::TextList(text_list.clone());
assert_eq!(op_int.as_integer(), Ok(int));
assert_eq!(op_int_list.as_integer_list(), Ok(int_list.as_slice()));
assert_eq!(op_text.as_text(), Ok(text.as_str()));
assert_eq!(op_text_list.as_text_list(), Ok(text_list.as_slice()));
[op_int, op_int_list, op_text, op_text_list]
.into_iter()
.for_each(|op| {
let op_json = format!("{op}");
assert_eq!(serde_json::to_string(&op).unwrap(), op_json);
assert_eq!(serde_json::from_str::<Operand>(&op_json).unwrap(), op);
});
});
}
}