use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Timestamp(DateTime<Utc>);
impl Timestamp {
pub fn now() -> Self {
Self(Utc::now())
}
pub fn from_datetime(dt: DateTime<Utc>) -> Self {
Self(dt)
}
pub fn as_datetime(&self) -> DateTime<Utc> {
self.0
}
pub fn duration_since(&self, other: Timestamp) -> chrono::Duration {
self.0.signed_duration_since(other.0)
}
}
impl Default for Timestamp {
fn default() -> Self {
Self::now()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum StructuredContent {
Text(String),
Json(serde_json::Value),
Binary(Vec<u8>),
Composite(Vec<StructuredContent>),
}
impl StructuredContent {
pub fn text(content: impl Into<String>) -> Self {
Self::Text(content.into())
}
pub fn json(value: serde_json::Value) -> Self {
Self::Json(value)
}
pub fn binary(data: Vec<u8>) -> Self {
Self::Binary(data)
}
pub fn as_text(&self) -> Option<&str> {
match self {
Self::Text(s) => Some(s),
_ => None,
}
}
pub fn as_json(&self) -> Option<&serde_json::Value> {
match self {
Self::Json(v) => Some(v),
_ => None,
}
}
}
impl Default for StructuredContent {
fn default() -> Self {
Self::Text(String::new())
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct ProvenanceChain {
pub id: Uuid,
pub parents: Vec<Uuid>,
pub created_at: Timestamp,
}
impl ProvenanceChain {
pub fn new() -> Self {
Self {
id: Uuid::new_v4(),
parents: Vec::new(),
created_at: Timestamp::now(),
}
}
pub fn from_parent(parent_id: Uuid) -> Self {
Self {
id: Uuid::new_v4(),
parents: vec![parent_id],
created_at: Timestamp::now(),
}
}
pub fn with_parent(mut self, parent_id: Uuid) -> Self {
self.parents.push(parent_id);
self
}
pub fn merge(parents: Vec<Uuid>) -> Self {
Self {
id: Uuid::new_v4(),
parents,
created_at: Timestamp::now(),
}
}
}
impl From<Uuid> for ProvenanceChain {
fn from(id: Uuid) -> Self {
Self::from_parent(id)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timestamp_creation() {
let ts = Timestamp::now();
std::thread::sleep(std::time::Duration::from_millis(1));
let ts2 = Timestamp::now();
assert!(ts2 > ts);
}
#[test]
fn test_structured_content() {
let content = StructuredContent::text("Hello, world!");
assert_eq!(content.as_text(), Some("Hello, world!"));
assert_eq!(content.as_json(), None);
}
#[test]
fn test_provenance_chain() {
let chain = ProvenanceChain::new();
assert!(chain.parents.is_empty());
let parent_id = Uuid::new_v4();
let child = ProvenanceChain::from_parent(parent_id);
assert_eq!(child.parents, vec![parent_id]);
}
}