wme-models 0.1.3

Type definitions for the Wikimedia Enterprise API
Documentation
#![doc = include_str!("../README.md")]

/// Article types including Article and StructuredArticle.
pub mod article;

/// Content types including ArticleBody, Image, and License.
pub mod content;

/// Schema envelope for version compatibility.
pub mod envelope;

/// Error types for model operations.
pub mod error;

/// Metadata types including EventMetadata, EventType, and identifiers.
pub mod metadata;

/// Reference and citation types.
pub mod reference;

/// Structured content types including Infobox, Section, and Table.
pub mod structured;

/// Version and editor information types.
pub mod version;

/// Request parameter types for API queries.
pub mod request;

pub use article::{Article, ProjectRef, StructuredArticle, Visibility};
pub use content::{ArticleBody, Image, License};
pub use envelope::ArticleEnvelope;
pub use error::ModelError;
pub use metadata::{
    BatchInfo, ChunkInfo, EventMetadata, EventType, Language, Namespace, Project, ProjectInfo,
    ProjectType, RealtimeBatchInfo, RealtimeProject, SimplifiedLanguage, SimplifiedNamespace, Size,
    SnapshotInfo,
};
pub use reference::{Citation, Reference};
pub use request::{Filter, FilterValue, RequestParams};
pub use structured::{Infobox, Section, Table, TableReference};
pub use version::{
    ArticleSize, Editor, MaintenanceTags, PreviousVersion, Protection, ReferenceNeed,
    ReferenceRisk, RevertRisk, Scores, Version,
};

use serde::{Deserialize, Serialize};

/// Wikidata entity reference.
///
/// # Examples
///
/// ```
/// use wme_models::WikidataEntity;
///
/// let entity = WikidataEntity {
///     identifier: "Q42".to_string(),
///     url: "https://www.wikidata.org/entity/Q42".to_string(),
/// };
///
/// assert_eq!(entity.identifier, "Q42");
/// ```
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct WikidataEntity {
    /// Entity identifier (e.g., "Q42")
    pub identifier: String,
    /// Wikidata URL
    pub url: String,
}

/// Wikidata entity reference with aspects (used in Realtime API).
///
/// Aspects indicate how the entity is used in the article:
/// - "S" - Sitelinks are used
/// - "L" - Label is used
/// - "D" - Description is used
/// - "C" - Statements are used
/// - "X" - All aspects are used
/// - "O" - Something else is used
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct RealtimeWikidataEntity {
    /// Entity identifier (e.g., "Q42")
    pub identifier: String,
    /// Wikidata URL
    pub url: String,
    /// Usage aspects (e.g., "S", "O", "C")
    pub aspects: Vec<String>,
}

/// Wikidata entity usage information.
///
/// # Examples
///
/// ```
/// use wme_models::WikidataEntityUsage;
///
/// let usage = WikidataEntityUsage {
///     identifier: "P31".to_string(),
///     url: "https://www.wikidata.org/entity/P31".to_string(),
///     aspects: vec!["C".to_string()],
/// };
/// ```
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct WikidataEntityUsage {
    /// Entity identifier
    pub identifier: String,
    /// Wikidata URL
    pub url: String,
    /// Usage aspects (e.g., "S", "O", "C")
    pub aspects: Vec<String>,
}

/// Category reference.
///
/// Categories group articles by topic within Wikimedia projects.
///
/// # Examples
///
/// ```
/// use wme_models::Category;
///
/// let category = Category {
///     name: "Category:Mammals".to_string(),
///     url: "https://en.wikipedia.org/wiki/Category:Mammals".to_string(),
/// };
/// ```
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Category {
    /// Category name
    pub name: String,
    /// Category URL
    pub url: String,
}

/// Template reference.
///
/// Templates are reusable wikitext snippets used across articles.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Template {
    /// Template name
    pub name: String,
    /// Template URL
    pub url: String,
}

/// Redirect reference.
///
/// Redirects are alternative names that point to this article.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Redirect {
    /// Target article name
    pub name: String,
    /// Target URL
    pub url: String,
}

/// Link within content.
///
/// Links connect articles within and across Wikimedia projects.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Link {
    /// Link text
    pub text: String,
    /// Link URL
    pub url: String,
}

/// Snapshot identifier: {language}{project}_namespace_{number}
///
/// # Examples
///
/// ```
/// use wme_models::SnapshotIdentifier;
///
/// // Create from components
/// let id = SnapshotIdentifier::new("en", "wiki", 0);
/// assert_eq!(id.to_string(), "enwiki_namespace_0");
///
/// // Parse from string
/// let id: SnapshotIdentifier = "enwiki_namespace_0".parse().unwrap();
/// assert_eq!(id.language, "en");
/// assert_eq!(id.project, "wiki");
/// assert_eq!(id.namespace, 0);
///
/// // Use predefined constants
/// let enwiki = SnapshotIdentifier::enwiki_namespace_0();
/// let dewiki = SnapshotIdentifier::dewiki_namespace_0();
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SnapshotIdentifier {
    /// Language code (e.g., "en", "de")
    pub language: String,
    /// Project code (e.g., "wiki", "wiktionary")
    pub project: String,
    /// Namespace number
    pub namespace: u32,
}

impl SnapshotIdentifier {
    /// Create a new snapshot identifier.
    ///
    /// # Examples
    ///
    /// ```
    /// use wme_models::SnapshotIdentifier;
    ///
    /// let id = SnapshotIdentifier::new("fr", "wiki", 0);
    /// assert_eq!(id.to_string(), "frwiki_namespace_0");
    /// ```
    pub fn new(language: &str, project: &str, namespace: u32) -> Self {
        Self {
            language: language.to_string(),
            project: project.to_string(),
            namespace,
        }
    }

    /// English Wikipedia articles (namespace 0).
    pub fn enwiki_namespace_0() -> Self {
        Self::new("en", "wiki", 0)
    }

    /// German Wikipedia articles.
    pub fn dewiki_namespace_0() -> Self {
        Self::new("de", "wiki", 0)
    }
}

impl std::str::FromStr for SnapshotIdentifier {
    type Err = error::ModelError;

    /// Parse from string (e.g., "enwiki_namespace_0").
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split("_namespace_").collect();
        if parts.len() != 2 {
            return Err(error::ModelError::InvalidSnapshotId(s.to_string()));
        }

        let namespace = parts[1]
            .parse::<u32>()
            .map_err(|_| error::ModelError::InvalidSnapshotId(s.to_string()))?;

        let lang_project = parts[0];

        // Extract language and project by matching known project suffixes
        // Order matters: check longer names first (wiktionary before wiki)
        let project_suffixes = [
            "wiktionary",
            "wikibooks",
            "wikinews",
            "wikiquote",
            "wikisource",
            "wikiversity",
            "wikivoyage",
            "commons",
            "wiki",
        ];

        let (language, project) = project_suffixes
            .iter()
            .find_map(|&suffix| {
                lang_project
                    .strip_suffix(suffix)
                    .map(|lang| (lang.to_string(), suffix.to_string()))
            })
            .ok_or_else(|| error::ModelError::InvalidSnapshotId(s.to_string()))?;

        Ok(Self {
            language,
            project,
            namespace,
        })
    }
}

impl std::fmt::Display for SnapshotIdentifier {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}{}_namespace_{}",
            self.language, self.project, self.namespace
        )
    }
}