use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use super::validate::{Validate, Validator};
#[cfg_attr(feature = "v2_3_0", doc = "```rust")]
#[cfg_attr(not(feature = "v2_3_0"), doc = "```rust,ignore")]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Extensions(BTreeMap<String, serde_json::Value>);
impl Extensions {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn get_raw(&self, key: &str) -> Option<&serde_json::Value> {
self.0.get(key)
}
pub fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<Option<T>, serde_json::Error> {
self.0.get(key).cloned().map(serde_json::from_value).transpose()
}
pub fn insert<T: Serialize>(
&mut self,
key: impl Into<String>,
value: T,
) -> Result<(), serde_json::Error> {
self.0.insert(key.into(), serde_json::to_value(value)?);
Ok(())
}
pub fn remove(&mut self, key: &str) -> Option<serde_json::Value> {
self.0.remove(key)
}
#[must_use]
pub fn contains_key(&self, key: &str) -> bool {
self.0.contains_key(key)
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &serde_json::Value)> {
self.0.iter()
}
pub fn keys(&self) -> impl Iterator<Item = &String> {
self.0.keys()
}
}
impl Validate for Extensions {
fn validate_in(&self, _v: &mut Validator) {}
}
impl<K: Into<String>, V: Into<serde_json::Value>> FromIterator<(K, V)> for Extensions {
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
Self(iter.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
}
}
impl<'a> IntoIterator for &'a Extensions {
type Item = (&'a String, &'a serde_json::Value);
type IntoIter = std::collections::btree_map::Iter<'a, String, serde_json::Value>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[cfg(feature = "schema")]
impl schemars::JsonSchema for Extensions {
fn schema_name() -> std::borrow::Cow<'static, str> {
"Extensions".into()
}
fn json_schema(_g: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "object",
"additionalProperties": true,
"description": "Undocumented JSON fields, preserved verbatim",
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn typed_access_round_trips() {
let mut ext = Extensions::new();
ext.insert("nltnm_rank", 7u32).unwrap();
assert_eq!(ext.get::<u32>("nltnm_rank").unwrap(), Some(7));
assert_eq!(ext.get::<u32>("absent").unwrap(), None);
assert!(ext.get::<String>("nltnm_rank").is_err(), "type mismatch is an error");
}
#[test]
fn empty_extensions_are_invisible() {
let ext = Extensions::new();
assert!(ext.is_empty());
assert_eq!(serde_json::to_string(&ext).unwrap(), "{}");
}
}