use crate::rpcvalue::RpcValue;
use std::fmt;
use crate::{CponWriter, Writer};
#[derive(Debug, Clone, PartialEq)]
pub enum MetaKey {
Int(i32),
Str(String),
}
impl From<&GetKey<'_>> for MetaKey {
fn from(k: &GetKey) -> Self {
match k {
GetKey::Int(i) => MetaKey::Int(*i),
GetKey::Str(s) => MetaKey::Str((*s).to_string()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct MetaKeyVal {
pub(crate) key: MetaKey,
pub(crate) value: RpcValue
}
#[derive(Copy, Clone)]
pub enum GetKey<'a> {
Int(i32),
Str(&'a str),
}
pub trait GetIndex {
fn make_key(&self) -> GetKey<'_>;
}
impl GetIndex for &str {
fn make_key(&self) -> GetKey<'_> {
GetKey::Str(self)
}
}
impl GetIndex for i32 {
fn make_key(&self) -> GetKey<'_> {
GetKey::Int(*self)
}
}
impl GetIndex for u32 {
fn make_key(&self) -> GetKey<'_> {
#[expect(clippy::cast_possible_wrap, reason = "We hope that the key is small enough to fit")]
GetKey::Int(*self as i32)
}
}
impl GetIndex for usize {
fn make_key(&self) -> GetKey<'_> {
#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap, reason = "We hope that the key is small enough to fit")]
GetKey::Int(*self as i32)
}
}
#[derive(PartialEq, Clone)]
pub struct MetaMap(pub(crate) Vec<MetaKeyVal>);
impl Default for MetaMap {
fn default() -> Self {
Self::new()
}
}
impl MetaMap {
pub fn new() -> MetaMap {
MetaMap(Vec::new())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
self.0.len()
}
#[expect(clippy::needless_pass_by_value, reason = "GetIndex is implemented for a lot of types, and some might not be owned")]
pub fn insert<I>(&mut self, key: I, value: RpcValue) -> &mut Self
where I: GetIndex
{
match self.find(&key) {
None => {
let key = key.make_key();
let key = MetaKey::from(&key);
self.0.push(MetaKeyVal{key, value });
},
Some(ix) => self.0.get_mut(ix).expect("The value has been found with .find()").value = value,
}
self
}
#[expect(clippy::needless_pass_by_value, reason = "GetIndex is implemented for a lot of types, and some might not be owned")]
pub fn remove<I>(&mut self, key: I) -> Option<RpcValue>
where I: GetIndex
{
match self.find(&key) {
None => None,
Some(ix) => Some(self.0.remove(ix).value),
}
}
#[expect(clippy::needless_pass_by_value, reason = "GetIndex is implemented for a lot of types, and some might not be owned")]
pub fn get<I>(&self, key: I) -> Option<&RpcValue>
where I: GetIndex
{
self.find(&key).map(|ix| &self.0.get(ix).expect("The value has been found with .find()").value)
}
fn find<I>(&self, key: &I) -> Option<usize>
where I: GetIndex
{
let key_to_search = key.make_key();
for (ix, kv) in self.0.iter().enumerate() {
match key_to_search {
GetKey::Int(key_to_search) => {
match &kv.key {
MetaKey::Int(key) if *key == key_to_search => {return Some(ix)}
_ => { }
}
}
GetKey::Str(key_to_search) => {
match &kv.key {
MetaKey::Str(key) if key == key_to_search => { return Some(ix) }
_ => { }
}
}
}
}
None
}
}
impl fmt::Debug for MetaMap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl fmt::Display for MetaMap {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut buff: Vec<u8> = Vec::new();
let mut wr = CponWriter::new(&mut buff);
let res = wr.write_meta(self);
if let Err(e) = res {
log::warn!("to_cpon write with error: {e}");
return write!(fmt, "<invalid>")
}
match String::from_utf8(buff) {
Ok(s) => write!(fmt, "{s}"),
Err(_) => write!(fmt, "<invalid>"),
}
}
}
#[cfg(test)]
mod test {
use crate::metamap::MetaMap;
use crate::rpcvalue::RpcValue;
use std::collections::BTreeMap;
#[test]
fn metamap_insert() {
let mut mm = MetaMap::new();
mm.insert(123, RpcValue::from(1.1));
assert_eq!(mm.get(123).unwrap_or_default().as_f64(), 1.1);
mm.insert("foo", RpcValue::from("bar")).insert(123, RpcValue::from("baz"));
assert_eq!(mm.get("foo").unwrap_or_default().as_str(), "bar");
assert_eq!(mm.get(123).unwrap_or_default().as_str(), "baz");
let v1 = vec![RpcValue::from("foo"), RpcValue::from("bar"), RpcValue::from("baz")];
let v2 = v1.clone();
mm.insert("list", RpcValue::from(v1));
assert_eq!(mm.get("list").unwrap_or_default().as_list(), &v2);
let mut v1: BTreeMap<i32, RpcValue> = BTreeMap::new();
v1.insert(1, RpcValue::from("foo"));
v1.insert(2, RpcValue::from("bar"));
v1.insert(3, RpcValue::from("baz"));
let v2 = v1.clone();
mm.insert("imap", RpcValue::from(v1));
assert_eq!(mm.get("imap").unwrap_or_default().as_imap(), &v2);
let mut v1: BTreeMap<String, RpcValue> = BTreeMap::new();
v1.insert("a".to_string(), RpcValue::from("foo"));
v1.insert("b".to_string(), RpcValue::from("bar"));
v1.insert("c".to_string(), RpcValue::from("baz"));
let v2 = v1.clone();
mm.insert("map", RpcValue::from(v1));
assert_eq!(mm.get("map").unwrap_or_default().as_map(), &v2);
}
}