use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::content::ArticleBody;
use crate::metadata::{EventMetadata, Language};
use crate::reference::Reference;
use crate::structured::{Infobox, Section, Table};
use crate::version::{OptionalPreviousVersion, Protection, Version};
use crate::{
Category, Image, License, Namespace, Redirect, Template, WikidataEntity, WikidataEntityUsage,
};
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Visibility {
pub text: bool,
pub editor: bool,
pub comment: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct ProjectRef {
pub identifier: String,
pub url: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Article {
pub identifier: u64,
pub name: String,
pub url: String,
#[serde(rename = "abstract")]
pub abstract_text: Option<String>,
pub description: Option<String>,
pub date_modified: DateTime<Utc>,
pub date_previously_modified: Option<DateTime<Utc>>,
pub in_language: Language,
pub is_part_of: ProjectRef,
pub namespace: Option<Namespace>,
pub main_entity: Option<WikidataEntity>,
pub additional_entities: Option<Vec<WikidataEntityUsage>>,
pub categories: Option<Vec<Category>>,
pub templates: Option<Vec<Template>>,
pub redirects: Option<Vec<Redirect>>,
pub version: Version,
#[serde(default)]
pub previous_version: OptionalPreviousVersion,
pub watchers_count: Option<u64>,
pub protection: Option<Vec<Protection>>,
pub visibility: Option<Visibility>,
pub image: Option<Image>,
pub license: Vec<License>,
pub article_body: Option<ArticleBody>,
pub event: Option<EventMetadata>,
pub has_parts: Option<Vec<Section>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct StructuredArticle {
#[serde(flatten)]
pub base: Article,
pub date_created: DateTime<Utc>,
pub infoboxes: Vec<Infobox>,
pub sections: Vec<Section>,
pub tables: Table,
pub references: Reference,
}
impl StructuredArticle {
pub fn infobox(&self, name: &str) -> Option<&Infobox> {
self.infoboxes
.iter()
.find(|i| i.name.as_ref().map(|n| n == name).unwrap_or(false))
}
pub fn section(&self, name: &str) -> Option<&Section> {
self.sections
.iter()
.find(|s| s.name.as_ref().map(|n| n == name).unwrap_or(false))
}
pub fn table(&self, identifier: &str) -> Option<&Table> {
if self.tables.identifier == identifier {
Some(&self.tables)
} else {
None
}
}
}
impl std::ops::Deref for StructuredArticle {
type Target = Article;
fn deref(&self) -> &Self::Target {
&self.base
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::version::{ArticleSize, Editor};
use chrono::Utc;
fn create_test_language() -> Language {
Language {
identifier: Some("en".to_string()),
name: Some("English".to_string()),
alternate_name: None,
direction: Some("ltr".to_string()),
}
}
fn create_test_namespace() -> Namespace {
Namespace {
identifier: 0,
name: Some("".to_string()),
description: Some("Main namespace".to_string()),
}
}
fn create_test_project_ref() -> ProjectRef {
ProjectRef {
identifier: "enwiki".to_string(),
url: Some("https://en.wikipedia.org".to_string()),
}
}
fn create_test_version() -> Version {
Version {
identifier: 1182847293,
editor: Some(Editor {
identifier: Some(12345),
name: Some("TestUser".to_string()),
is_bot: Some(false),
is_anonymous: Some(false),
date_started: Some(Utc::now()),
edit_count: Some(1000),
groups: Some(vec!["user".to_string()]),
is_admin: Some(false),
is_patroller: Some(false),
has_advanced_rights: Some(false),
}),
comment: Some("Test edit".to_string()),
tags: Some(vec!["mobile edit".to_string()]),
has_tag_needs_citation: Some(false),
is_minor_edit: Some(false),
is_flagged_stable: Some(true),
is_breaking_news: Some(false),
noindex: Some(false),
number_of_characters: Some(5000),
size: Some(ArticleSize {
value: 15000,
unit_text: "B".to_string(),
}),
maintenance_tags: None,
scores: None,
}
}
fn create_test_article() -> Article {
Article {
identifier: 28492,
name: "Squirrel".to_string(),
url: "https://en.wikipedia.org/wiki/Squirrel".to_string(),
abstract_text: Some("Squirrels are members of the family Sciuridae...".to_string()),
description: Some("Family of rodents".to_string()),
date_modified: Utc::now(),
date_previously_modified: None,
in_language: create_test_language(),
is_part_of: create_test_project_ref(),
namespace: Some(create_test_namespace()),
main_entity: None,
additional_entities: None,
categories: None,
templates: None,
redirects: None,
version: create_test_version(),
previous_version: OptionalPreviousVersion(None),
watchers_count: Some(42),
protection: None,
visibility: None,
image: None,
license: vec![],
article_body: None,
event: None,
has_parts: None,
}
}
#[test]
fn test_article_creation() {
let article = create_test_article();
assert_eq!(article.identifier, 28492);
assert_eq!(article.name, "Squirrel");
assert!(article.abstract_text.is_some());
}
#[test]
fn test_project_ref_creation() {
let project = ProjectRef {
identifier: "enwiki".to_string(),
url: Some("https://en.wikipedia.org".to_string()),
};
assert_eq!(project.identifier, "enwiki");
assert!(project.url.is_some());
}
#[test]
fn test_visibility_creation() {
let visibility = Visibility {
text: true,
editor: false,
comment: false,
};
assert!(visibility.text);
assert!(!visibility.editor);
assert!(!visibility.comment);
}
#[test]
fn test_version_comparison_for_dedup() {
let article1 = create_test_article();
let article2 = Article {
identifier: 28492,
name: "Squirrel".to_string(),
url: "https://en.wikipedia.org/wiki/Squirrel".to_string(),
abstract_text: None,
description: None,
date_modified: Utc::now(),
date_previously_modified: None,
in_language: create_test_language(),
is_part_of: create_test_project_ref(),
namespace: Some(create_test_namespace()),
main_entity: None,
additional_entities: None,
categories: None,
templates: None,
redirects: None,
version: Version {
identifier: 1182847294, editor: create_test_version().editor,
comment: None,
tags: None,
has_tag_needs_citation: None,
is_minor_edit: None,
is_flagged_stable: None,
is_breaking_news: None,
noindex: None,
number_of_characters: None,
size: None,
maintenance_tags: None,
scores: None,
},
previous_version: OptionalPreviousVersion(None),
watchers_count: None,
protection: None,
visibility: None,
image: None,
license: vec![],
article_body: None,
event: None,
has_parts: None,
};
assert!(article2.version.identifier > article1.version.identifier);
}
}