use crate::serde::byte_map_as_strings;
use indexmap::IndexMap;
use itertools::Itertools;
use serde::Deserialize;
use serde::Serialize;
#[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Meta {
#[serde(with = "byte_map_as_strings")]
data: IndexMap<Vec<u8>, Vec<u8>, ahash::RandomState>,
}
const INTERNAL_META_KEYS: &[&[u8]] = &[
b"internal-builtin",
b"internal-content",
b"internal-description",
b"internal-format",
b"internal-hide",
b"internal-insert",
b"internal-label",
b"internal-order",
b"internal-placeholder",
b"internal-safe",
b"internal-serialize",
b"internal-sort",
b"internal-taken",
b"internal-toggle",
b"internal-variant",
];
impl core::fmt::Debug for Meta {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self == &Meta::default() {
return write!(f, "Meta::default()");
}
let mut f = f.debug_struct("Meta");
for (k, v) in &self.data {
f.field(&String::from_utf8_lossy(k), &String::from_utf8_lossy(v));
}
f.finish()
}
}
impl core::fmt::Display for Meta {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let u8l = |d| String::from_utf8_lossy(d).into_owned();
let iter = self.data.iter().map(|(k, v)| [u8l(k), u8l(v)]);
f.write_str(&iter.map(|p| p.join(": ")).join(", "))
}
}
impl core::hash::Hash for Meta {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
for (k, v) in &self.data {
k.hash(state);
v.hash(state);
}
}
}
impl Meta {
pub fn new<K: Into<Vec<u8>>, V: Into<Vec<u8>>>(key: K, value: V) -> Self {
let data = [(key.into(), value.into())];
let data = data.into_iter().collect();
Self { data }
}
pub(crate) fn into_keys(self) -> Vec<Vec<u8>> {
self.data.into_keys().collect()
}
pub fn flag<K: Into<Vec<u8>>>(&mut self, key: K) {
self.push(key, String::default());
}
pub fn push<K: Into<Vec<u8>>, V: Into<Vec<u8>>>(&mut self, key: K, value: V) {
let key = key.into();
let value = value.into();
validate_internal_meta_key(&key);
self.data.insert(key, value);
}
pub(crate) fn append(&mut self, other: Meta) {
for (k, v) in other.data {
self.push(k, v);
}
}
pub(crate) fn remove(&mut self, key: &[u8]) -> Option<Vec<u8>> {
validate_internal_meta_key(key);
self.data.swap_remove(key)
}
pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
validate_internal_meta_key(key);
self.data.get(key).map(Vec::as_slice)
}
pub fn contains(&self, key: &[u8]) -> bool {
validate_internal_meta_key(key);
self.data.contains_key(key)
}
pub(crate) fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
fn validate_internal_meta_key(k: &[u8]) {
debug_assert!(
!unknown_internal_meta_key(k),
"unknown internal meta key: {}",
String::from_utf8_lossy(k)
);
}
fn unknown_internal_meta_key(k: &[u8]) -> bool {
k.starts_with(b"internal-") && !INTERNAL_META_KEYS.contains(&k)
}