mod bibliography;
mod citation;
mod entity;
mod glossary;
mod jsonld;
pub use bibliography::{
Author, Bibliography, BibliographyEntry, CitationStyle, EntryType, PartialDate,
};
pub use citation::{Citation, Footnote, LocatorType};
pub use entity::{EntityLink, EntityType, KnowledgeBase};
pub use glossary::{Glossary, GlossaryRef, GlossaryTerm};
pub use jsonld::JsonLdMetadata;
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_bibliography_new() {
let bib = Bibliography::new(CitationStyle::Apa);
assert!(bib.is_empty());
assert_eq!(bib.style, CitationStyle::Apa);
}
#[test]
fn test_bibliography_add_entry() {
let mut bib = Bibliography::default();
let entry = BibliographyEntry::new("smith2023", EntryType::Article, "A Great Paper");
bib.add_entry(entry);
assert_eq!(bib.len(), 1);
assert!(bib.contains("smith2023"));
assert!(!bib.contains("jones2024"));
}
#[test]
fn test_bibliography_entry_builder() {
let entry =
BibliographyEntry::new("smith2023", EntryType::Article, "Deep Learning Advances")
.with_author(Author::new("John", "Smith"))
.with_author(Author::new("Jane", "Doe"))
.with_issued(PartialDate::year(2023))
.with_container("Nature")
.with_volume_issue("100", Some("5".to_string()))
.with_pages("123-145")
.with_doi("10.1234/nature.2023.1234");
assert_eq!(entry.id, "smith2023");
assert_eq!(entry.authors.len(), 2);
assert_eq!(entry.container_title, Some("Nature".to_string()));
assert_eq!(entry.doi, Some("10.1234/nature.2023.1234".to_string()));
}
#[test]
fn test_author_display_name() {
let author1 = Author::new("John", "Smith");
assert_eq!(author1.display_name(), "Smith, John");
let author2 = Author::literal("World Health Organization");
assert_eq!(author2.display_name(), "World Health Organization");
}
#[test]
fn test_partial_date_display() {
assert_eq!(PartialDate::year(2023).to_string(), "2023");
assert_eq!(PartialDate::year_month(2023, 6).to_string(), "2023-06");
assert_eq!(PartialDate::full(2023, 6, 15).to_string(), "2023-06-15");
assert_eq!(
PartialDate::seasonal(2023, "Spring").to_string(),
"Spring 2023"
);
}
#[test]
fn test_citation_new() {
let cite = Citation::new("smith2023");
assert_eq!(cite.refs, vec!["smith2023"]);
assert_eq!(cite.first_ref(), Some("smith2023"));
assert!(!cite.suppress_author);
}
#[test]
fn test_citation_with_page() {
let cite = Citation::new("smith2023")
.with_page("42")
.with_prefix("see")
.with_suffix("for details");
assert_eq!(cite.locator, Some("42".to_string()));
assert_eq!(cite.locator_type, Some(LocatorType::Page));
assert_eq!(cite.prefix, Some("see".to_string()));
}
#[test]
fn test_glossary_new() {
let glossary = Glossary::new();
assert!(glossary.is_empty());
}
#[test]
fn test_glossary_add_term() {
let mut glossary = Glossary::default();
let term = GlossaryTerm::new(
"ai",
"Artificial Intelligence",
"The simulation of human intelligence by machines.",
);
glossary.add_term(term);
assert_eq!(glossary.len(), 1);
assert!(glossary.get("ai").is_some());
}
#[test]
fn test_glossary_find_by_text() {
let mut glossary = Glossary::new();
glossary.add_term(
GlossaryTerm::new("ml", "Machine Learning", "A subset of AI.")
.with_alias("ML")
.with_alias("statistical learning"),
);
assert!(glossary.find_by_text("Machine Learning").is_some());
assert!(glossary.find_by_text("ML").is_some());
assert!(glossary.find_by_text("ml").is_some());
assert!(glossary.find_by_text("Deep Learning").is_none());
}
#[test]
fn test_glossary_term_builder() {
let term = GlossaryTerm::new("api", "API", "Application Programming Interface")
.with_alias("Application Programming Interface")
.with_see_also("rest")
.with_category("Computing")
.with_pronunciation("/ˌeɪpiˈaɪ/");
assert_eq!(term.aliases.len(), 1);
assert_eq!(term.see_also, vec!["rest"]);
assert_eq!(term.category, Some("Computing".to_string()));
}
#[test]
fn test_entity_link_new() {
let link = EntityLink::new("https://example.org/entity/123", EntityType::Person);
assert_eq!(link.entity_type, EntityType::Person);
}
#[test]
fn test_entity_link_wikipedia() {
let link = EntityLink::wikipedia("Albert Einstein", EntityType::Person);
assert!(link.uri.contains("Albert_Einstein"));
assert_eq!(link.source, Some(KnowledgeBase::Wikipedia));
}
#[test]
fn test_entity_link_wikidata() {
let link = EntityLink::wikidata("Q937", EntityType::Person);
assert!(link.uri.contains("Q937"));
assert_eq!(link.source, Some(KnowledgeBase::Wikidata));
}
#[test]
fn test_entity_link_confidence() {
let link =
EntityLink::new("https://example.org", EntityType::Concept).with_confidence(0.95);
assert_eq!(link.confidence, Some(0.95));
let link2 =
EntityLink::new("https://example.org", EntityType::Concept).with_confidence(1.5);
assert_eq!(link2.confidence, Some(1.0));
}
#[test]
fn test_jsonld_new() {
let jsonld = JsonLdMetadata::new();
assert_eq!(jsonld.context, vec!["https://schema.org"]);
assert!(jsonld.graph.is_empty());
}
#[test]
fn test_jsonld_add_node() {
let mut jsonld = JsonLdMetadata::new();
jsonld.add_node(json!({
"@type": "Person",
"name": "John Smith"
}));
assert_eq!(jsonld.graph.len(), 1);
}
#[test]
fn test_jsonld_scholarly_article() {
let entry = BibliographyEntry::new("test", EntryType::Article, "Test Paper")
.with_author(Author::new("John", "Doe"))
.with_issued(PartialDate::year(2023))
.with_doi("10.1234/test");
let article = JsonLdMetadata::scholarly_article(&entry);
assert_eq!(article["@type"], "ScholarlyArticle");
assert_eq!(article["name"], "Test Paper");
}
#[test]
fn test_bibliography_serialization() {
let mut bib = Bibliography::new(CitationStyle::Chicago);
bib.add_entry(BibliographyEntry::new("test", EntryType::Book, "Test Book"));
let json = serde_json::to_string(&bib).unwrap();
assert!(json.contains("\"style\":\"chicago\""));
assert!(json.contains("\"entryType\":\"book\""));
}
#[test]
fn test_citation_serialization() {
let cite = Citation::new("smith2023").with_page("42");
let json = serde_json::to_string(&cite).unwrap();
assert!(json.contains("\"refs\":[\"smith2023\"]"));
assert!(json.contains("\"locator\":\"42\""));
}
#[test]
fn test_citation_multi() {
let cite = Citation::multi(vec!["smith2023".into(), "jones2024".into()]);
assert_eq!(cite.refs(), &["smith2023", "jones2024"]);
assert_eq!(cite.first_ref(), Some("smith2023"));
}
#[test]
fn test_citation_backward_compat_singular_ref() {
let json = r#"{"ref":"smith2023","locator":"42","locatorType":"page"}"#;
let cite: Citation = serde_json::from_str(json).unwrap();
assert_eq!(cite.refs, vec!["smith2023"]);
assert_eq!(cite.locator, Some("42".to_string()));
}
#[test]
fn test_citation_multi_refs_roundtrip() {
let cite = Citation::multi(vec!["smith2023".into(), "jones2024".into()]).with_page("42");
let json = serde_json::to_string(&cite).unwrap();
let parsed: Citation = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.refs, vec!["smith2023", "jones2024"]);
assert_eq!(parsed.locator, Some("42".to_string()));
}
#[test]
fn test_footnote_new() {
let fn1 = Footnote::new(1);
assert_eq!(fn1.number, 1);
assert_eq!(fn1.id, None);
assert_eq!(fn1.content, None);
assert!(fn1.children.is_empty());
}
#[test]
fn test_footnote_builder() {
let fn1 = Footnote::new(1)
.with_id("fn-1")
.with_content("This is the footnote text.");
assert_eq!(fn1.number, 1);
assert_eq!(fn1.id, Some("fn-1".to_string()));
assert_eq!(fn1.content, Some("This is the footnote text.".to_string()));
}
#[test]
fn test_footnote_serialization() {
let fn1 = Footnote::new(1).with_content("A footnote.");
let json_str = serde_json::to_string(&fn1).unwrap();
assert!(json_str.contains("\"number\":1"));
assert!(json_str.contains("\"content\":\"A footnote.\""));
assert!(!json_str.contains("\"id\""));
}
#[test]
fn test_footnote_deserialization() {
let json_str = r#"{"number":2,"id":"fn-2","content":"Some text."}"#;
let fn2: Footnote = serde_json::from_str(json_str).unwrap();
assert_eq!(fn2.number, 2);
assert_eq!(fn2.id, Some("fn-2".to_string()));
assert_eq!(fn2.content, Some("Some text.".to_string()));
}
#[test]
fn test_footnote_roundtrip() {
let original = Footnote::new(3)
.with_id("fn-3")
.with_content("Round-trip test.");
let json_str = serde_json::to_string(&original).unwrap();
let deserialized: Footnote = serde_json::from_str(&json_str).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_footnote_with_children() {
use crate::content::{Block, Text};
let para = Block::paragraph(vec![Text::plain("Rich footnote content.")]);
let fn1 = Footnote::new(1).with_id("fn-1").with_children(vec![para]);
assert!(fn1.has_children());
assert!(!fn1.has_content());
assert_eq!(fn1.children.len(), 1);
}
#[test]
fn test_footnote_children_serialization() {
use crate::content::{Block, Text};
let para = Block::paragraph(vec![Text::plain("Footnote text.")]);
let fn1 = Footnote::new(1).with_children(vec![para]);
let json_str = serde_json::to_string(&fn1).unwrap();
assert!(json_str.contains("\"number\":1"));
assert!(json_str.contains("\"children\""));
assert!(json_str.contains("\"type\":\"paragraph\""));
assert!(!json_str.contains("\"content\""));
}
#[test]
fn test_footnote_children_deserialization() {
let json_str = r#"{
"number": 1,
"id": "fn-1",
"children": [
{
"type": "paragraph",
"children": [{"value": "Rich content."}]
}
]
}"#;
let fn1: Footnote = serde_json::from_str(json_str).unwrap();
assert_eq!(fn1.number, 1);
assert_eq!(fn1.id, Some("fn-1".to_string()));
assert!(fn1.has_children());
assert_eq!(fn1.children.len(), 1);
}
#[test]
fn test_footnote_has_content() {
let fn1 = Footnote::new(1).with_content("Text");
assert!(fn1.has_content());
assert!(!fn1.has_children());
}
}