use std::{collections::HashMap, fmt};
#[cfg(feature = "serde")]
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{self, Visitor},
};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum Value {
Int(i64),
Float(f64),
Bool(bool),
String(String),
}
impl From<i64> for Value {
fn from(i: i64) -> Self {
Self::Int(i)
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
Self::Float(f)
}
}
impl From<bool> for Value {
fn from(v: bool) -> Self {
Value::Bool(v)
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Self::String(s)
}
}
impl From<&'_ str> for Value {
fn from(s: &'_ str) -> Self {
Self::String(s.to_string())
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Int(i) => write!(f, "{}", i),
Value::Float(fl) => write!(f, "{}", fl),
Value::Bool(b) => write!(f, "{}", b),
Value::String(s) => {
let needs_quotes = s.is_empty()
|| !s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
|| s.chars()
.next()
.map(|c| c.is_ascii_digit())
.unwrap_or(false);
if needs_quotes {
write!(f, "\"")?;
for c in s.chars() {
match c {
'"' => write!(f, "\\\"")?,
'\\' => write!(f, "\\\\")?,
'\n' => write!(f, "\\n")?,
'\r' => write!(f, "\\r")?,
'\t' => write!(f, "\\t")?,
c => write!(f, "{}", c)?,
}
}
write!(f, "\"")
} else {
write!(f, "{}", s)
}
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CompositeValue {
Single(Value),
List(Vec<Value>),
Dict(Vec<(String, Value)>),
}
impl<T: Into<Value>> From<T> for CompositeValue {
fn from(v: T) -> Self {
Self::Single(v.into())
}
}
impl<T: Into<Value>> From<Vec<T>> for CompositeValue {
fn from(v: Vec<T>) -> Self {
Self::List(v.into_iter().map(|item| item.into()).collect())
}
}
impl<T: Into<Value>> From<HashMap<String, T>> for CompositeValue {
fn from(v: HashMap<String, T>) -> Self {
Self::Dict(v.into_iter().map(|(k, v)| (k, v.into())).collect())
}
}
impl<T: Into<Value>> FromIterator<T> for CompositeValue {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self::List(iter.into_iter().map(|item| item.into()).collect())
}
}
impl<T: Into<Value>> FromIterator<(String, T)> for CompositeValue {
fn from_iter<I: IntoIterator<Item = (String, T)>>(iter: I) -> Self {
Self::Dict(iter.into_iter().map(|(k, v)| (k, v.into())).collect())
}
}
impl fmt::Display for CompositeValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompositeValue::Single(value) => write!(f, "{}", value),
CompositeValue::List(values) => {
for (i, value) in values.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", value)?;
}
Ok(())
}
CompositeValue::Dict(entries) => {
for (i, (key, value)) in entries.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", key, value)?;
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Parameter {
Basic(Value),
Composite(String, CompositeValue),
}
impl<T: Into<Value>> From<T> for Parameter {
fn from(v: T) -> Self {
Self::Basic(v.into())
}
}
impl<K: Into<String>, V: Into<CompositeValue>> From<(K, V)> for Parameter {
fn from(v: (K, V)) -> Self {
Self::Composite(v.0.into(), v.1.into())
}
}
impl fmt::Display for Parameter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Parameter::Basic(value) => write!(f, "{}", value),
Parameter::Composite(name, value) => write!(f, "{}({})", name, value),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Command {
pub name: String,
pub params: Vec<Parameter>,
}
impl Command {
pub fn new(name: impl Into<String>, params: Vec<Parameter>) -> Self {
Self {
name: name.into(),
params,
}
}
pub fn new_text(content: impl Into<String>) -> Self {
Self::new("@text", vec![Parameter::from(content.into())])
}
pub fn new_annotation(content: impl Into<String>) -> Self {
Self::new("@annotation", vec![Parameter::from(content.into())])
}
pub fn new_number(value: i64, args: Vec<Parameter>) -> Self {
let mut all_args = vec![Parameter::from(value)];
all_args.extend(args);
Self::new("@number", all_args)
}
pub fn name(&self) -> &str {
&self.name
}
pub fn params(&self) -> &[Parameter] {
&self.params
}
}
impl fmt::Display for Command {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?;
for param in self.params.iter() {
write!(f, " ")?;
write!(f, "{}", param)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_display() {
let cmd = Command::new("hello", vec![Parameter::Basic("world".to_string().into())]);
assert_eq!(format!("{}", cmd), "hello world");
}
#[test]
fn test_command_display_text() {
let cmd = Command::new_text("hello world");
assert_eq!(format!("{}", cmd), "@text \"hello world\"");
}
#[test]
fn test_command_display_annotation() {
let cmd = Command::new_annotation("hello world".to_string());
assert_eq!(format!("{}", cmd), "@annotation \"hello world\"");
}
#[test]
fn test_convert_value() {
let cv = Parameter::from(10);
assert_eq!(format!("{}", cv), "10");
let cv = Parameter::from(("a", 10));
assert_eq!(format!("{}", cv), "a(10)");
}
#[test]
fn test_value_display_escaping() {
let v = Value::String("quote \" and backslash \\".to_string());
assert_eq!(format!("{}", v), "\"quote \\\" and backslash \\\\\"");
let v = Value::String("newline \n and tab \t".to_string());
assert_eq!(format!("{}", v), "\"newline \\n and tab \\t\"");
}
#[test]
fn test_float_display() {
let v = Value::Float(1.23);
assert_eq!(format!("{}", v), "1.23");
}
#[test]
fn test_composite_value_conversions() {
let vec_int = vec![1, 2, 3];
let cv: CompositeValue = CompositeValue::from(vec_int);
if let CompositeValue::List(list) = cv {
assert_eq!(list.len(), 3);
assert_eq!(list[0], Value::Int(1));
} else {
panic!("Expected List");
}
let iter = vec![4, 5, 6].into_iter();
let cv: CompositeValue = iter.collect();
if let CompositeValue::List(list) = cv {
assert_eq!(list.len(), 3);
assert_eq!(list[0], Value::Int(4));
} else {
panic!("Expected List");
}
let mut map = HashMap::new();
map.insert("k1".to_string(), 1);
let cv: CompositeValue = CompositeValue::from(map);
if let CompositeValue::Dict(entries) = cv {
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].0, "k1");
assert_eq!(entries[0].1, Value::Int(1));
} else {
panic!("Expected Dict");
}
let map_iter = vec![("k2".to_string(), 2)].into_iter();
let cv: CompositeValue = map_iter.collect();
if let CompositeValue::Dict(entries) = cv {
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].0, "k2");
} else {
panic!("Expected Dict");
}
}
#[test]
fn test_composite_value_display() {
let list = CompositeValue::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
assert_eq!(format!("{}", list), "1, 2, 3");
let dict = CompositeValue::Dict(vec![
("key1".to_string(), Value::Int(1)),
("key2".to_string(), Value::String("value".to_string())),
]);
assert_eq!(format!("{}", dict), "key1: 1, key2: value");
let single = CompositeValue::Single(Value::Int(42));
assert_eq!(format!("{}", single), "42");
}
}
#[cfg(feature = "serde")]
impl Serialize for CompositeValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
CompositeValue::Single(v) => v.serialize(serializer),
CompositeValue::List(l) => l.serialize(serializer),
CompositeValue::Dict(d) => {
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(d.len()))?;
for (k, v) in d {
map.serialize_entry(k, v)?;
}
map.end()
}
}
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for CompositeValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct CompositeValueVisitor;
impl<'de> Visitor<'de> for CompositeValueVisitor {
type Value = CompositeValue;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a value, list, or dictionary")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(CompositeValue::Single(Value::Int(v)))
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(CompositeValue::Single(Value::Float(v)))
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(CompositeValue::Single(Value::Bool(v)))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(CompositeValue::Single(Value::String(v.to_string())))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(CompositeValue::Single(Value::String(v)))
}
fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
where
V: de::SeqAccess<'de>,
{
let mut values = Vec::new();
while let Some(value) = visitor.next_element()? {
values.push(value);
}
Ok(CompositeValue::List(values))
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: de::MapAccess<'de>,
{
let mut entries = Vec::new();
while let Some((key, value)) = access.next_entry()? {
entries.push((key, value));
}
Ok(CompositeValue::Dict(entries))
}
}
deserializer.deserialize_any(CompositeValueVisitor)
}
}
#[cfg(feature = "serde")]
impl Serialize for Parameter {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Parameter::Basic(v) => v.serialize(serializer),
Parameter::Composite(k, v) => {
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry(k, v)?;
map.end()
}
}
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Parameter {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct ParameterVisitor;
impl<'de> Visitor<'de> for ParameterVisitor {
type Value = Parameter;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a basic value or a named composite parameter")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Parameter::Basic(Value::Int(v)))
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Parameter::Basic(Value::Float(v)))
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Parameter::Basic(Value::Bool(v)))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Parameter::Basic(Value::String(v.to_string())))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Parameter::Basic(Value::String(v)))
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: de::MapAccess<'de>,
{
if let Some((key, value)) = access.next_entry()? {
if access.next_entry::<String, CompositeValue>()?.is_some() {
return Err(de::Error::custom(
"Composite parameter map must have exactly one entry",
));
}
Ok(Parameter::Composite(key, value))
} else {
Err(de::Error::custom("Composite parameter map cannot be empty"))
}
}
}
deserializer.deserialize_any(ParameterVisitor)
}
}