use std::fmt;
use schemars::JsonSchema;
use serde::de::{self, DeserializeOwned, Deserializer};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use crate::vocabulary::Vocabulary;
#[derive(Debug, Clone, PartialEq, Serialize, JsonSchema)]
#[serde(bound = "")]
#[schemars(
bound = "V::Source: JsonSchema, V::Dimensions: JsonSchema, V::Labels: JsonSchema",
rename = "Envelope"
)]
pub struct Envelope<V: Vocabulary> {
pub v: u32,
pub ts: String,
pub stream: String,
pub seq: u64,
pub source: V::Source,
pub kind: Kind,
#[serde(flatten)]
pub dimensions: V::Dimensions,
#[serde(default)]
pub labels: V::Labels,
#[serde(default)]
pub payload: Map<String, Value>,
#[serde(default)]
pub artifacts: Vec<ArtifactRef>,
}
impl<'de, V: Vocabulary> Deserialize<'de> for Envelope<V> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let mut fields: Map<String, Value> = Map::deserialize(deserializer)?;
let v = take(&mut fields, "v")?;
let ts = take(&mut fields, "ts")?;
let stream = take(&mut fields, "stream")?;
let seq = take(&mut fields, "seq")?;
let source = take(&mut fields, "source")?;
let kind = take(&mut fields, "kind")?;
let labels = take_or_default(&mut fields, "labels")?;
let payload = take_or_default(&mut fields, "payload")?;
let artifacts = take_or_default(&mut fields, "artifacts")?;
let dimensions =
serde_json::from_value(Value::Object(fields)).map_err(de::Error::custom)?;
Ok(Self {
v,
ts,
stream,
seq,
source,
kind,
dimensions,
labels,
payload,
artifacts,
})
}
}
pub(crate) fn take<T: DeserializeOwned, E: de::Error>(
fields: &mut Map<String, Value>,
key: &'static str,
) -> Result<T, E> {
let value = fields.remove(key).ok_or_else(|| E::missing_field(key))?;
serde_json::from_value(value).map_err(|failure| E::custom(format!("{key}: {failure}")))
}
pub(crate) fn take_or_default<T: DeserializeOwned + Default, E: de::Error>(
fields: &mut Map<String, Value>,
key: &'static str,
) -> Result<T, E> {
match fields.remove(key) {
Some(value) => {
serde_json::from_value(value).map_err(|failure| E::custom(format!("{key}: {failure}")))
}
None => Ok(T::default()),
}
}
impl<V: Vocabulary> Envelope<V> {
#[must_use]
pub fn order_key(&self) -> (&str, &str, u64) {
(&self.ts, &self.stream, self.seq)
}
}
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
)]
#[serde(transparent)]
pub struct Kind(pub String);
impl Kind {
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for Kind {
fn from(kind: &str) -> Self {
Self(kind.to_owned())
}
}
impl From<String> for Kind {
fn from(kind: String) -> Self {
Self(kind)
}
}
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
)]
#[serde(transparent)]
pub struct Source(pub String);
impl Source {
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for Source {
fn from(source: &str) -> Self {
Self(source.to_owned())
}
}
impl From<String> for Source {
fn from(source: String) -> Self {
Self(source)
}
}
impl fmt::Display for Source {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(transparent)]
pub struct Labels(pub Map<String, Value>);
impl Labels {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn get_str(&self, key: &str) -> Option<&str> {
self.0.get(key).and_then(Value::as_str)
}
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<Value>) -> &mut Self {
self.0.insert(key.into(), value.into());
self
}
#[must_use]
pub fn with(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
self.insert(key, value);
self
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct NoDimensions {}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ArtifactRef {
pub id: String,
pub kind: String,
pub bytes: u64,
}