use eva_common::prelude::*;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TagId {
Str(String),
Num(u64),
}
impl TagId {
pub fn as_str(&self) -> Option<&str> {
match self {
TagId::Str(v) => Some(v.as_str()),
TagId::Num(_) => None,
}
}
}
impl fmt::Display for TagId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TagId::Str(v) => write!(f, "{}", v),
TagId::Num(v) => write!(f, "{}", v),
}
}
}
impl From<&str> for TagId {
#[inline]
fn from(s: &str) -> Self {
Self::Str(s.to_owned())
}
}
impl From<String> for TagId {
#[inline]
fn from(s: String) -> Self {
Self::Str(s)
}
}
impl From<u64> for TagId {
#[inline]
fn from(u: u64) -> Self {
Self::Num(u)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Range {
from: Option<usize>,
to: Option<usize>,
}
impl Range {
pub fn new(from: Option<usize>, to: Option<usize>) -> Self {
Self { from, to }
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Tag {
pub id: TagId,
pub range: Range,
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)?;
if self.has_range() {
write!(f, "[")?;
let mut written = false;
if let Some(len) = self.range_len() {
if len == 1 {
write!(f, "{}", self.range.from.unwrap_or_default())?;
written = true;
}
}
if !written {
if let Some(from) = self.range.from {
write!(f, "{}", from)?;
}
write!(f, "-")?;
if let Some(to) = self.range.to {
write!(f, "{}", to)?;
}
}
write!(f, "]")?;
}
Ok(())
}
}
macro_rules! impl_to_tag {
($t: ty) => {
impl From<$t> for Tag {
#[inline]
fn from(s: $t) -> Self {
Self::new0(s.into())
}
}
};
}
impl_to_tag!(&str);
impl_to_tag!(String);
impl_to_tag!(u64);
impl Tag {
#[inline]
pub fn new(id: TagId, range: Range) -> Self {
Self { id, range }
}
#[inline]
pub fn new0(id: TagId) -> Self {
Self {
id,
range: Range::default(),
}
}
#[inline]
pub fn has_range(&self) -> bool {
self.range.from.is_some() || self.range.to.is_some()
}
pub fn range_len(&self) -> Option<usize> {
self.range
.to
.map(|to| to - self.range.from.unwrap_or_default() + 1)
}
}
fn parse_range(s: &str) -> EResult<Range> {
if let Some(pos) = s.find('-') {
let f = &s[..pos];
let t = &s[pos + 1..];
let from = if f.is_empty() { None } else { Some(f.parse()?) };
let to = if t.is_empty() { None } else { Some(t.parse()?) };
if let Some(f) = from {
if let Some(t) = to {
if f > t {
return Err(Error::invalid_params("invalid seq index"));
}
}
}
Ok(Range::new(from, to))
} else {
let n: usize = s.parse()?;
Ok(Range::new(Some(n), Some(n)))
}
}
impl FromStr for Tag {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(x) = s.strip_suffix(']') {
if let Some(pos) = x.rfind('[') {
let range = parse_range(&x[pos + 1..])?;
Ok(Tag::new(x[..pos].into(), range))
} else {
Err(Error::invalid_params("invalid array"))
}
} else {
Ok(Tag::new0(s.into()))
}
}
}
#[derive(Debug, Clone, Default)]
pub struct TagMap {
tags: BTreeMap<TagId, Value>,
}
impl Serialize for TagMap {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.tags.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TagMap {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let tags: BTreeMap<TagId, Value> = Deserialize::deserialize(deserializer)?;
Ok(Self { tags })
}
}
impl TagMap {
pub fn tags(&self) -> &BTreeMap<TagId, Value> {
&self.tags
}
pub fn tag_mut(&mut self) -> &mut BTreeMap<TagId, Value> {
&mut self.tags
}
pub fn delete(&mut self, tag: Tag) -> EResult<()> {
if tag.has_range() {
Err(Error::invalid_params("can not delete range"))
} else {
self.tags.remove(&tag.id);
Ok(())
}
}
pub fn get(&self, tag: &Tag) -> EResult<Value> {
if let Some(val) = self.tags.get(&tag.id) {
if tag.has_range() {
if let Value::Seq(seq) = val {
if let Some(len) = tag.range_len() {
if len == 1 {
return Ok(seq
.get(tag.range.from.unwrap_or_default())
.map_or(Value::Unit, Clone::clone));
}
}
let from = tag.range.from.unwrap_or_default();
let to = tag.range.to.map_or_else(|| seq.len(), |v| v + 1);
let mut result = Vec::with_capacity(to - from + 1);
for i in from..to {
result.push(seq.get(i).map_or(Value::Unit, Clone::clone));
}
Ok(Value::Seq(result))
} else {
Err(Error::invalid_data("tag is not a seq"))
}
} else {
Ok(val.clone())
}
} else {
Err(Error::not_found("no such tag"))
}
}
pub fn set(&mut self, tag: Tag, value: Value) -> EResult<()> {
if tag.has_range() {
if let Some(val) = self.tags.get_mut(&tag.id) {
if let Value::Seq(seq) = val {
if let Some(len) = tag.range_len() {
if len == 1 {
let idx = tag.range.from.unwrap_or_default();
if seq.len() < idx + 1 {
seq.resize(idx + 1, Value::Unit);
}
seq[idx] = value;
} else if let Value::Seq(s) = value {
if s.len() != len {
return Err(Error::invalid_params("invalid value seq len"));
}
let last_idx = tag.range.to.unwrap_or_default() + 1;
let tail = if last_idx > seq.len() {
None
} else {
Some(seq.split_off(last_idx))
};
let first_idx = tag.range.from.unwrap_or_default();
seq.resize(first_idx, Value::Unit);
seq.extend(s);
if let Some(t) = tail {
seq.extend(t);
}
} else {
return Err(Error::invalid_params("value is not a seq"));
}
} else if let Value::Seq(s) = value {
let idx = tag.range.from.unwrap_or_default();
seq.resize(idx, Value::Unit);
seq.extend(s);
} else {
return Err(Error::invalid_params("value is not a seq"));
}
} else {
return Err(Error::invalid_params("tag is not an array"));
}
} else if let Value::Seq(seq) = value {
let len = if let Some(len) = tag.range_len() {
if len != seq.len() {
return Err(Error::invalid_params("invalid value seq len"));
}
len
} else {
tag.range.from.unwrap_or_default() + seq.len()
};
let mut result = Vec::with_capacity(len);
result.resize(tag.range.from.unwrap_or_default(), Value::Unit);
result.extend(seq);
self.tags.insert(tag.id, Value::Seq(result));
} else if let Some(len) = tag.range_len() {
if len == 1 {
let idx = tag.range.from.unwrap_or_default();
let mut result = vec![Value::Unit; idx + 1];
result[idx] = value;
self.tags.insert(tag.id, Value::Seq(result));
} else {
return Err(Error::invalid_params("value is not a seq"));
}
} else {
return Err(Error::invalid_params("value is not a seq"));
}
} else {
self.tags.insert(tag.id, value);
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::Tag;
#[test]
fn test_parse_display() {
let tag: Tag = "test".parse().unwrap();
assert_eq!(tag.to_string(), "test");
let tag: Tag = "test[1]".parse().unwrap();
assert_eq!(tag.to_string(), "test[1]");
let tag: Tag = "test[-1]".parse().unwrap();
assert_eq!(tag.to_string(), "test[-1]");
let tag: Tag = "test[1-]".parse().unwrap();
assert_eq!(tag.to_string(), "test[1-]");
let tag: Tag = "test[1-5]".parse().unwrap();
assert_eq!(tag.to_string(), "test[1-5]");
}
}