use std::ops::{Deref, DerefMut}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use super::{ RawAudioContent, RawContent, RawEmbeddedResource, RawImageContent, RawResource, RawResourceTemplate, RawTextContent, Role, }; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[non_exhaustive] pub struct Annotations { #[serde(skip_serializing_if = "Option::is_none")] pub audience: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub priority: Option, #[serde(skip_serializing_if = "Option::is_none", rename = "lastModified")] pub last_modified: Option>, } impl Annotations { /// Creates a new Annotations instance specifically for resources /// optional priority, and a timestamp (defaults to now if None) pub fn for_resource(priority: f32, timestamp: DateTime) -> Self { assert!( (0.0..=1.0).contains(&priority), "Priority {priority} must be between 0.0 and 1.0" ); Annotations { priority: Some(priority), last_modified: Some(timestamp), audience: None, } } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Annotated { #[serde(flatten)] pub raw: T, #[serde(skip_serializing_if = "Option::is_none")] pub annotations: Option, } impl Deref for Annotated { type Target = T; fn deref(&self) -> &Self::Target { &self.raw } } impl DerefMut for Annotated { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.raw } } impl Annotated { pub fn new(raw: T, annotations: Option) -> Self { Self { raw, annotations } } pub fn remove_annotation(&mut self) -> Option { self.annotations.take() } pub fn audience(&self) -> Option<&Vec> { self.annotations.as_ref().and_then(|a| a.audience.as_ref()) } pub fn priority(&self) -> Option { self.annotations.as_ref().and_then(|a| a.priority) } pub fn timestamp(&self) -> Option> { self.annotations.as_ref().and_then(|a| a.last_modified) } pub fn with_audience(self, audience: Vec) -> Annotated where Self: Sized, { if let Some(annotations) = self.annotations { Annotated { raw: self.raw, annotations: Some(Annotations { audience: Some(audience), ..annotations }), } } else { Annotated { raw: self.raw, annotations: Some(Annotations { audience: Some(audience), priority: None, last_modified: None, }), } } } pub fn with_priority(self, priority: f32) -> Annotated where Self: Sized, { if let Some(annotations) = self.annotations { Annotated { raw: self.raw, annotations: Some(Annotations { priority: Some(priority), ..annotations }), } } else { Annotated { raw: self.raw, annotations: Some(Annotations { priority: Some(priority), last_modified: None, audience: None, }), } } } pub fn with_timestamp(self, timestamp: DateTime) -> Annotated where Self: Sized, { if let Some(annotations) = self.annotations { Annotated { raw: self.raw, annotations: Some(Annotations { last_modified: Some(timestamp), ..annotations }), } } else { Annotated { raw: self.raw, annotations: Some(Annotations { last_modified: Some(timestamp), priority: None, audience: None, }), } } } pub fn with_timestamp_now(self) -> Annotated where Self: Sized, { self.with_timestamp(Utc::now()) } } mod sealed { pub trait Sealed {} } macro_rules! annotate { ($T: ident) => { impl sealed::Sealed for $T {} impl AnnotateAble for $T {} }; } annotate!(RawContent); annotate!(RawTextContent); annotate!(RawImageContent); annotate!(RawAudioContent); annotate!(RawEmbeddedResource); annotate!(RawResource); annotate!(RawResourceTemplate); pub trait AnnotateAble: sealed::Sealed { fn optional_annotate(self, annotations: Option) -> Annotated where Self: Sized, { Annotated::new(self, annotations) } fn annotate(self, annotations: Annotations) -> Annotated where Self: Sized, { Annotated::new(self, Some(annotations)) } fn no_annotation(self) -> Annotated where Self: Sized, { Annotated::new(self, None) } fn with_audience(self, audience: Vec) -> Annotated where Self: Sized, { self.annotate(Annotations { audience: Some(audience), ..Default::default() }) } fn with_priority(self, priority: f32) -> Annotated where Self: Sized, { self.annotate(Annotations { priority: Some(priority), ..Default::default() }) } fn with_timestamp(self, timestamp: DateTime) -> Annotated where Self: Sized, { self.annotate(Annotations { last_modified: Some(timestamp), ..Default::default() }) } fn with_timestamp_now(self) -> Annotated where Self: Sized, { self.with_timestamp(Utc::now()) } }