mwbot 0.7.1

A MediaWiki bot framework
Documentation
// SPDX-FileCopyrightText: 2023 Bingwu Zhang <xtexchooser@duck.com>
// SPDX-FileCopyrightText: 2023 Kunal Mehta <legoktm@debian.org>
// SPDX-License-Identifier: GPL-3.0-or-later
//! Generators related to categories
//!
//! See the [`Categories`] and [`CategoryMembers`] type documentation
//! for specifics.
use super::{Generator, ParamValue, SortDirection};
use mwtimestamp::Timestamp;

/// Get all categories that included in the pages.
///
/// See [API documentation](https://www.mediawiki.org/wiki/API:Categories) for more details.
#[derive(Generator)]
#[params(generator = "categories", gcllimit = "max")]
pub struct Categories {
    #[param("titles")]
    titles: Vec<String>,
    #[param("gclcategories")]
    categories: Option<Vec<String>>,
    #[param("gclshow")]
    filter: Option<Filter>,
    #[param("gcldir")]
    sort: Option<SortDirection>,
}

pub enum Filter {
    Hidden,
    NonHidden,
}

impl ParamValue for Filter {
    fn stringify(&self) -> String {
        match self {
            Self::Hidden => "hidden",
            Self::NonHidden => "!hidden",
        }
        .to_string()
    }
}

/// Get pages that are members of a category
///
/// See [API documentation](https://www.mediawiki.org/wiki/API:Categorymembers) for more details.
#[derive(Generator)]
#[params(generator = "categorymembers", gcmlimit = "max")]
pub struct CategoryMembers {
    /// Title of the category
    #[param("gcmtitle")]
    title: String,
    /// Get results from pages in these namespaces
    #[param("gcmnamespace")]
    namespace: Option<Vec<u32>>,
    /// Direction to get results in
    #[param("gcmdir")]
    dir: Option<SortDirection>,
    /// Get results that are these category member types
    #[param("gcmtype")]
    type_: Option<CategoryMemberType>,
    /// How to sort category members
    #[param("gcmsort")]
    sort: Option<CategoryMemberSort>,
    #[param("gcmstarthexsortkey")]
    starthexsortkey: Option<String>,
    #[param("gcmendhexsortkey")]
    endhexsortkey: Option<String>,
    #[param("gcmstartsortkeyprefix")]
    startsortkeyprefix: Option<String>,
    #[param("gcmendsortkeyprefix")]
    endsortkeyprefix: Option<String>,
    #[param("gcmstart")]
    start: Option<Timestamp>,
    #[param("gcmend")]
    end: Option<Timestamp>,
}

#[derive(Default)]
pub enum CategoryMemberType {
    Page,
    Subcat,
    File,
    #[default]
    All,
}

impl ParamValue for CategoryMemberType {
    fn stringify(&self) -> String {
        match self {
            Self::Page => "page",
            Self::Subcat => "subcat",
            Self::File => "file",
            Self::All => "page|subcat|file",
        }
        .to_string()
    }
}

#[derive(Default)]
pub enum CategoryMemberSort {
    #[default]
    Sortkey,
    Timestamp,
}

impl ParamValue for CategoryMemberSort {
    fn stringify(&self) -> String {
        match self {
            Self::Sortkey => "sortkey",
            Self::Timestamp => "timestamp",
        }
        .to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::testwp;

    #[tokio::test]
    async fn test_categories() {
        let bot = testwp().await;
        let gen = Categories::new(vec!["Mwbot-rs/Categorized".to_string()]);
        dbg!(gen.params());
        let mut pages = gen.generate(&bot);

        let mut found = Vec::new();

        while let Some(page) = pages.recv().await {
            let page = page.unwrap();
            dbg!(page.title());

            found.push(page.title().to_string());
        }

        assert!(found.contains(&"Category:Mwbot-rs".to_string()));
    }

    #[tokio::test]
    async fn test_categorymembers() {
        let bot = testwp().await;
        let mut members =
            CategoryMembers::new("Category:!Requests".to_string())
                .generate(&bot);
        while let Some(page) = members.recv().await {
            let page = page.unwrap();
            // This page is the last one, so it should require at least one continuation
            if page.title() == "Category:Unsuccessful requests for permissions"
            {
                // found it!
                return;
            }
        }

        panic!("Unable to find the page");
    }
}