use crate::core::Timestamp;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum SourceType {
Article,
Report,
Witness,
Sensor,
Social,
Academic,
Government,
Archive,
#[default]
Other,
}
impl std::fmt::Display for SourceType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SourceType::Article => write!(f, "article"),
SourceType::Report => write!(f, "report"),
SourceType::Witness => write!(f, "witness"),
SourceType::Sensor => write!(f, "sensor"),
SourceType::Social => write!(f, "social"),
SourceType::Academic => write!(f, "academic"),
SourceType::Government => write!(f, "government"),
SourceType::Archive => write!(f, "archive"),
SourceType::Other => write!(f, "other"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SourceRef {
pub source_type: SourceType,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub date: Option<Timestamp>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
}
impl SourceRef {
pub fn new(source_type: SourceType) -> Self {
Self {
source_type,
url: None,
title: None,
author: None,
date: None,
notes: None,
}
}
pub fn article(url: impl Into<String>) -> Self {
Self {
source_type: SourceType::Article,
url: Some(url.into()),
title: None,
author: None,
date: None,
notes: None,
}
}
pub fn report(url: impl Into<String>) -> Self {
Self {
source_type: SourceType::Report,
url: Some(url.into()),
title: None,
author: None,
date: None,
notes: None,
}
}
pub fn witness(notes: Option<String>) -> Self {
Self {
source_type: SourceType::Witness,
url: None,
title: None,
author: None,
date: None,
notes,
}
}
pub fn sensor(url: impl Into<String>) -> Self {
Self {
source_type: SourceType::Sensor,
url: Some(url.into()),
title: None,
author: None,
date: None,
notes: None,
}
}
pub fn builder() -> SourceRefBuilder {
SourceRefBuilder::new()
}
pub fn with_url(mut self, url: impl Into<String>) -> Self {
self.url = Some(url.into());
self
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn with_author(mut self, author: impl Into<String>) -> Self {
self.author = Some(author.into());
self
}
pub fn with_date(mut self, date: Timestamp) -> Self {
self.date = Some(date);
self
}
}
impl Default for SourceRef {
fn default() -> Self {
Self::new(SourceType::Other)
}
}
#[derive(Debug, Default)]
pub struct SourceRefBuilder {
source_type: SourceType,
url: Option<String>,
title: Option<String>,
author: Option<String>,
date: Option<Timestamp>,
notes: Option<String>,
}
impl SourceRefBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn source_type(mut self, source_type: SourceType) -> Self {
self.source_type = source_type;
self
}
pub fn url(mut self, url: impl Into<String>) -> Self {
self.url = Some(url.into());
self
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn author(mut self, author: impl Into<String>) -> Self {
self.author = Some(author.into());
self
}
pub fn date(mut self, date: Timestamp) -> Self {
self.date = Some(date);
self
}
pub fn notes(mut self, notes: impl Into<String>) -> Self {
self.notes = Some(notes.into());
self
}
pub fn build(self) -> SourceRef {
SourceRef {
source_type: self.source_type,
url: self.url,
title: self.title,
author: self.author,
date: self.date,
notes: self.notes,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_source_article() {
let source = SourceRef::article("https://example.com/news");
assert_eq!(source.source_type, SourceType::Article);
assert_eq!(source.url, Some("https://example.com/news".to_string()));
}
#[test]
fn test_source_builder() {
let source = SourceRef::builder()
.source_type(SourceType::Report)
.url("https://example.com/report.pdf")
.title("Annual Report")
.author("Research Team")
.build();
assert_eq!(source.source_type, SourceType::Report);
assert_eq!(source.title, Some("Annual Report".to_string()));
assert_eq!(source.author, Some("Research Team".to_string()));
}
#[test]
fn test_source_with_methods() {
let source = SourceRef::new(SourceType::Academic)
.with_url("https://journal.example.com/paper")
.with_title("Research Paper")
.with_author("Dr. Smith");
assert_eq!(source.source_type, SourceType::Academic);
assert!(source.url.is_some());
assert!(source.title.is_some());
}
#[test]
fn test_source_serialization() {
let source = SourceRef::article("https://example.com");
let json = serde_json::to_string(&source).unwrap();
let parsed: SourceRef = serde_json::from_str(&json).unwrap();
assert_eq!(source, parsed);
}
}