pub mod allpages;
pub mod allredirects;
pub mod categories;
pub mod file;
pub mod langlinks;
pub mod link;
#[cfg(feature = "linter")]
#[cfg_attr(docsrs, doc(cfg(feature = "linter")))]
pub mod lint;
pub mod log_events;
pub mod lonely;
pub mod pageswithprop;
pub mod querypage;
pub mod random;
pub mod recent_changes;
pub mod revisions;
pub mod search;
pub mod templates;
pub mod transcluded;
pub mod uncategorized;
#[cfg(feature = "wikibase")]
#[cfg_attr(docsrs, doc(cfg(feature = "wikibase")))]
pub mod unconnected_pages;
pub mod unused;
pub mod unwatched;
pub mod user_contribs;
mod value;
#[doc(hidden)]
pub mod __exports;
use crate::page::InfoResponseItem;
use crate::{Bot, Page, Result};
use categories::CategoryMembers;
pub use mwbot_derive::Generator;
use std::collections::HashMap;
use tokio::sync::mpsc::{self, Receiver};
pub use value::ParamValue;
#[doc(hidden)]
#[derive(Default, Debug)]
pub struct Params {
pub main: HashMap<String, String>,
pub continue_: HashMap<String, String>,
}
impl Params {
pub fn merged(&self) -> HashMap<&String, &String> {
let mut map = HashMap::new();
map.extend(&self.main);
map.extend(&self.continue_);
map
}
}
pub fn categorymembers_recursive(
bot: &Bot,
title: &str,
) -> Receiver<Result<Page>> {
let (tx, rx) = mpsc::channel(50);
let title = title.to_string();
let bot = bot.clone();
tokio::spawn(async move {
let mut seen = vec![];
let mut pending = vec![title];
while let Some(category) = pending.pop() {
seen.push(category.to_string());
#[allow(deprecated)]
let mut gen = CategoryMembers::new(&category).generate(&bot);
while let Some(page) = gen.recv().await {
if let Ok(page) = &page {
if page.is_category()
&& !seen.contains(&page.title().to_string())
&& !pending.contains(&page.title().to_string())
{
pending.push(page.title().to_string());
}
}
if tx.send(page).await.is_err() {
return;
}
}
}
});
rx
}
#[derive(Generator)]
#[params(generator = "embeddedin", geilimit = "max")]
pub struct EmbeddedIn {
#[param("geititle")]
title: String,
#[param("geinamespace")]
namespace: Option<Vec<u32>>,
#[param("geifilterredir")]
filter_redirect: Option<FilterRedirect>,
#[param("geidir")]
dir: Option<SortDirection>,
}
#[derive(Copy, Clone, Default)]
pub enum FilterRedirect {
#[default]
All,
Nonredirects,
Redirects,
}
impl ParamValue for FilterRedirect {
fn stringify(&self) -> String {
match self {
Self::All => "all",
Self::Nonredirects => "nonredirects",
Self::Redirects => "redirects",
}
.to_string()
}
}
#[derive(Copy, Clone, Debug, Default)]
pub enum SortDirection {
#[default]
Ascending,
Descending,
}
impl ParamValue for SortDirection {
fn stringify(&self) -> String {
match self {
Self::Ascending => "ascending",
Self::Descending => "descending",
}
.to_string()
}
}
pub trait Generator: Sized {
type Output;
fn params(&self) -> HashMap<&'static str, String>;
fn generate(self, bot: &Bot) -> Receiver<Self::Output>;
}
#[doc(hidden)]
pub fn transform_to_page(bot: &Bot, item: InfoResponseItem) -> Result<Page> {
bot.page(&item.title).inspect(|page| {
page.info.set(item).unwrap();
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::testwp;
#[tokio::test]
async fn test_categorymembers_recursive() {
let bot = testwp().await;
let mut members = categorymembers_recursive(&bot, "Category:Mwbot-rs");
let mut found = false;
while let Some(page) = members.recv().await {
if page.unwrap().title() == "Mwbot-rs/Categorized depth 1" {
found = true;
}
}
assert!(found, "Found depth 1 page");
}
#[tokio::test]
async fn test_embeddedin() {
let bot = testwp().await;
let mut embeddedin =
EmbeddedIn::new("Template:1x".to_string()).generate(&bot);
let mut count = 0;
while let Some(page) = embeddedin.recv().await {
page.unwrap();
count += 1;
if count == 5 {
break;
}
}
assert_eq!(count, 5);
}
}