pub mod chat;
pub mod document;
pub mod email;
pub mod email_clean;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Deserializer, Serialize};
use crate::memory::chunks::{Metadata, SourceRef};
pub(crate) fn deserialize_flexible_timestamp<'de, D>(
deserializer: D,
) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum RawTs {
Millis(i64),
Text(String),
}
let raw = RawTs::deserialize(deserializer)?;
match raw {
RawTs::Millis(ms) => chrono::TimeZone::timestamp_millis_opt(&Utc, ms)
.single()
.ok_or_else(|| serde::de::Error::custom(format!("invalid epoch-ms: {ms}"))),
RawTs::Text(s) => {
if let Ok(dt) = DateTime::parse_from_rfc3339(&s) {
return Ok(dt.with_timezone(&Utc));
}
if let Ok(ms) = s.parse::<i64>() {
return chrono::TimeZone::timestamp_millis_opt(&Utc, ms)
.single()
.ok_or_else(|| {
serde::de::Error::custom(format!("invalid epoch-ms string: {s}"))
});
}
Err(serde::de::Error::custom(format!(
"cannot parse '{s}' as RFC 3339 or epoch-ms"
)))
}
}
}
#[derive(Clone, Debug)]
pub struct CanonicalisedSource {
pub markdown: String,
pub metadata: Metadata,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CanonicaliseRequest<P> {
pub source_id: String,
#[serde(default)]
pub owner: String,
pub payload: P,
#[serde(default)]
pub tags: Vec<String>,
}
pub fn normalize_source_ref(source_ref: Option<String>) -> Option<SourceRef> {
source_ref.and_then(|value| {
let trimmed = value.trim();
if trimmed.is_empty() {
None
} else {
Some(SourceRef::new(trimmed.to_string()))
}
})
}