use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use super::message::{ReflectCow, ReflectMessage};
use super::DynamicMessage;
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Bool(bool),
I32(i32),
I64(i64),
U32(u32),
U64(u64),
F32(f32),
F64(f64),
String(String),
Bytes(Vec<u8>),
EnumNumber(i32),
Message(DynamicMessage),
List(Vec<Value>),
Map(MapValue),
}
#[derive(Debug)]
pub enum ValueRef<'a> {
Bool(bool),
I32(i32),
I64(i64),
U32(u32),
U64(u64),
F32(f32),
F64(f64),
String(&'a str),
Bytes(&'a [u8]),
EnumNumber(i32),
Message(ReflectCow<'a>),
List(&'a dyn ReflectList),
Map(&'a dyn ReflectMap),
}
impl Value {
#[must_use]
pub fn as_ref(&self) -> ValueRef<'_> {
match self {
Self::Bool(v) => ValueRef::Bool(*v),
Self::I32(v) => ValueRef::I32(*v),
Self::I64(v) => ValueRef::I64(*v),
Self::U32(v) => ValueRef::U32(*v),
Self::U64(v) => ValueRef::U64(*v),
Self::F32(v) => ValueRef::F32(*v),
Self::F64(v) => ValueRef::F64(*v),
Self::String(v) => ValueRef::String(v),
Self::Bytes(v) => ValueRef::Bytes(v),
Self::EnumNumber(v) => ValueRef::EnumNumber(*v),
Self::Message(v) => ValueRef::Message(ReflectCow::Borrowed(v as &dyn ReflectMessage)),
Self::List(v) => ValueRef::List(v),
Self::Map(v) => ValueRef::Map(v),
}
}
}
impl<'a> ValueRef<'a> {
#[must_use]
pub fn to_owned(&self) -> Value {
match self {
Self::Bool(v) => Value::Bool(*v),
Self::I32(v) => Value::I32(*v),
Self::I64(v) => Value::I64(*v),
Self::U32(v) => Value::U32(*v),
Self::U64(v) => Value::U64(*v),
Self::F32(v) => Value::F32(*v),
Self::F64(v) => Value::F64(*v),
Self::String(v) => Value::String((*v).into()),
Self::Bytes(v) => Value::Bytes((*v).into()),
Self::EnumNumber(v) => Value::EnumNumber(*v),
Self::Message(cow) => Value::Message(cow.to_dynamic()),
Self::List(v) => {
let mut out = Vec::with_capacity(v.len());
v.for_each(&mut |elem| out.push(elem.to_owned()));
Value::List(out)
}
Self::Map(v) => {
let mut out = Vec::with_capacity(v.len());
v.for_each(&mut |k, val| out.push((k.to_owned(), val.to_owned())));
Value::Map(MapValue::from_entries(out))
}
}
}
}
pub trait ReflectList: core::fmt::Debug {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn get(&self, idx: usize) -> Option<ValueRef<'_>>;
fn for_each(&self, f: &mut dyn FnMut(ValueRef<'_>));
}
pub trait ReflectMap: core::fmt::Debug {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn get(&self, key: &MapKey) -> Option<ValueRef<'_>>;
fn get_str(&self, key: &str) -> Option<ValueRef<'_>>;
fn for_each(&self, f: &mut dyn FnMut(MapKeyRef<'_>, ValueRef<'_>));
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MapKeyRef<'a> {
Bool(bool),
I32(i32),
I64(i64),
U32(u32),
U64(u64),
String(&'a str),
}
impl MapKeyRef<'_> {
#[must_use]
pub fn to_owned(self) -> MapKey {
match self {
Self::Bool(v) => MapKey::Bool(v),
Self::I32(v) => MapKey::I32(v),
Self::I64(v) => MapKey::I64(v),
Self::U32(v) => MapKey::U32(v),
Self::U64(v) => MapKey::U64(v),
Self::String(v) => MapKey::String(v.into()),
}
}
}
impl MapKey {
#[must_use]
pub fn as_ref(&self) -> MapKeyRef<'_> {
match self {
Self::Bool(v) => MapKeyRef::Bool(*v),
Self::I32(v) => MapKeyRef::I32(*v),
Self::I64(v) => MapKeyRef::I64(*v),
Self::U32(v) => MapKeyRef::U32(*v),
Self::U64(v) => MapKeyRef::U64(*v),
Self::String(v) => MapKeyRef::String(v),
}
}
}
impl ReflectMap for MapValue {
fn len(&self) -> usize {
Self::len(self)
}
fn get(&self, key: &MapKey) -> Option<ValueRef<'_>> {
Self::get(self, key).map(Value::as_ref)
}
fn get_str(&self, key: &str) -> Option<ValueRef<'_>> {
Self::get_str(self, key).map(Value::as_ref)
}
fn for_each(&self, f: &mut dyn FnMut(MapKeyRef<'_>, ValueRef<'_>)) {
for (k, v) in self.iter() {
f(k.as_ref(), v.as_ref());
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MapKey {
Bool(bool),
I32(i32),
I64(i64),
U32(u32),
U64(u64),
String(String),
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct MapValue {
entries: Vec<(MapKey, Value)>,
}
impl MapValue {
#[must_use]
pub const fn new() -> Self {
Self {
entries: Vec::new(),
}
}
#[must_use]
pub fn from_entries(mut entries: Vec<(MapKey, Value)>) -> Self {
entries.sort_by(|(a, _), (b, _)| a.cmp(b));
let mut deduped: Vec<(MapKey, Value)> = Vec::with_capacity(entries.len());
for entry in entries {
if let Some(last) = deduped.last_mut() {
if last.0 == entry.0 {
*last = entry;
continue;
}
}
deduped.push(entry);
}
Self { entries: deduped }
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[must_use]
pub fn get(&self, key: &MapKey) -> Option<&Value> {
self.entries
.binary_search_by(|(k, _)| k.cmp(key))
.ok()
.map(|i| &self.entries[i].1)
}
#[must_use]
pub fn get_str(&self, key: &str) -> Option<&Value> {
debug_assert!(
self.entries
.first()
.is_none_or(|(k, _)| matches!(k, MapKey::String(_))),
"get_str called on a non-string-keyed MapValue"
);
self.entries
.binary_search_by(|(k, _)| match k {
MapKey::String(s) => s.as_str().cmp(key),
_ => core::cmp::Ordering::Less,
})
.ok()
.and_then(|i| match &self.entries[i].0 {
MapKey::String(_) => Some(&self.entries[i].1),
_ => None,
})
}
#[must_use]
pub fn get_i64(&self, key: i64) -> Option<&Value> {
match self.entries.first().map(|(k, _)| k) {
Some(MapKey::I32(_)) => self.get(&MapKey::I32(i32::try_from(key).ok()?)),
Some(MapKey::I64(_)) => self.get(&MapKey::I64(key)),
Some(MapKey::U32(_)) => self.get(&MapKey::U32(u32::try_from(key).ok()?)),
Some(MapKey::U64(_)) => self.get(&MapKey::U64(u64::try_from(key).ok()?)),
_ => None,
}
}
pub fn insert(&mut self, key: MapKey, value: Value) {
match self.entries.binary_search_by(|(k, _)| k.cmp(&key)) {
Ok(i) => self.entries[i].1 = value,
Err(i) => self.entries.insert(i, (key, value)),
}
}
pub fn iter(&self) -> impl Iterator<Item = (&MapKey, &Value)> {
self.entries.iter().map(|(k, v)| (k, v))
}
#[must_use]
pub fn entries(&self) -> &[(MapKey, Value)] {
&self.entries
}
}
impl FromIterator<(MapKey, Value)> for MapValue {
fn from_iter<T: IntoIterator<Item = (MapKey, Value)>>(iter: T) -> Self {
Self::from_entries(iter.into_iter().collect())
}
}
impl<'a> IntoIterator for &'a MapValue {
type Item = (&'a MapKey, &'a Value);
type IntoIter = core::iter::Map<
core::slice::Iter<'a, (MapKey, Value)>,
fn(&'a (MapKey, Value)) -> (&'a MapKey, &'a Value),
>;
fn into_iter(self) -> Self::IntoIter {
self.entries.iter().map(|(k, v)| (k, v))
}
}
const _: () = {
assert!(core::mem::size_of::<ValueRef<'_>>() <= 32);
assert!(core::mem::size_of::<ReflectCow<'_>>() <= 24);
let _ = Box::<()>::new; };
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use alloc::vec;
#[test]
fn map_value_from_entries_dedup_keeps_last() {
let m = MapValue::from_entries(vec![
(MapKey::String("a".into()), Value::I32(1)),
(MapKey::String("b".into()), Value::I32(2)),
(MapKey::String("a".into()), Value::I32(3)),
]);
assert_eq!(m.len(), 2);
assert_eq!(m.get_str("a"), Some(&Value::I32(3)));
assert_eq!(m.get_str("b"), Some(&Value::I32(2)));
}
#[test]
fn map_value_get_str_no_alloc() {
let m = MapValue::from_entries(vec![
(MapKey::String("apple".into()), Value::I32(1)),
(MapKey::String("banana".into()), Value::I32(2)),
(MapKey::String("cherry".into()), Value::I32(3)),
]);
assert_eq!(m.get_str("banana"), Some(&Value::I32(2)));
assert_eq!(m.get_str("durian"), None);
}
#[test]
fn map_value_insert_maintains_sort() {
let mut m = MapValue::new();
m.insert(MapKey::String("z".into()), Value::I32(26));
m.insert(MapKey::String("a".into()), Value::I32(1));
m.insert(MapKey::String("m".into()), Value::I32(13));
m.insert(MapKey::String("a".into()), Value::I32(100)); let keys: Vec<_> = m.iter().map(|(k, _)| k.clone()).collect();
assert_eq!(
keys,
vec![
MapKey::String("a".into()),
MapKey::String("m".into()),
MapKey::String("z".into())
]
);
assert_eq!(m.get_str("a"), Some(&Value::I32(100)));
}
#[test]
fn map_value_get_i64() {
let m = MapValue::from_entries(vec![
(MapKey::I32(1), Value::String("one".to_string())),
(MapKey::I32(2), Value::String("two".to_string())),
]);
assert_eq!(m.get_i64(1), Some(&Value::String("one".to_string())));
assert_eq!(m.get_i64(3), None);
assert_eq!(m.get_i64(i64::MAX), None);
}
#[test]
fn map_value_const_empty() {
const EMPTY: MapValue = MapValue::new();
assert!(EMPTY.is_empty());
assert_eq!(EMPTY.get_str("anything"), None);
}
}