tools_interface/
pagepile.rs1use crate::{Site, Tool, ToolsError, fancy_title::FancyTitle};
14use async_trait::async_trait;
15use serde_json::{Value, json};
16
17#[derive(Debug, Default, PartialEq)]
18pub struct PagePile {
19 id: u32,
20
21 prefixed_titles: Vec<String>,
22 language: Option<String>,
23 project: Option<String>,
24 wiki: Option<String>,
25}
26
27impl PagePile {
28 pub fn new(id: u32) -> Self {
30 Self {
31 id,
32 ..Default::default()
33 }
34 }
35
36 pub fn prefixed_titles(&self) -> &[String] {
38 &self.prefixed_titles
39 }
40
41 pub fn language(&self) -> Option<&String> {
43 self.language.as_ref()
44 }
45
46 pub fn project(&self) -> Option<&String> {
48 self.project.as_ref()
49 }
50
51 pub fn wiki(&self) -> Option<&String> {
53 self.wiki.as_ref()
54 }
55
56 pub fn site(&self) -> Option<crate::Site> {
58 Some(match &self.wiki {
59 Some(wiki) => Site::from_wiki(wiki)?,
60 None => Site::from_language_project(self.language.as_ref()?, self.project.as_ref()?),
61 })
62 }
63
64 pub async fn as_json(&self) -> Option<Value> {
65 let site = self.site()?;
66 let api = site.api().await.ok()?;
67 Some(json!({
68 "pages": self.prefixed_titles()
69 .iter()
70 .map(|prefixed_title| FancyTitle::from_prefixed(prefixed_title, &api).to_json())
71 .collect::<Vec<Value>>(),
72 "site": site,
73 }))
74 }
75}
76
77#[async_trait]
78impl Tool for PagePile {
79 fn get_url(&self) -> String {
80 format!(
81 "https://pagepile.toolforge.org/api.php?id={id}&action=get_data&doit&format=json",
82 id = self.id
83 )
84 }
85
86 fn set_from_json(&mut self, j: Value) -> Result<(), ToolsError> {
87 self.language = j["language"].as_str().map(|s| s.to_string());
88 self.project = j["project"].as_str().map(|s| s.to_string());
89 self.wiki = j["wiki"].as_str().map(|s| s.to_string());
90 self.prefixed_titles = j["pages"]
91 .as_array()
92 .ok_or(ToolsError::Json("['pages'] has no rows array".into()))?
93 .iter()
94 .filter_map(|page| page.as_str())
95 .map(|prefixed_title| prefixed_title.to_string())
96 .collect();
97 let pages_returned = j["pages_returned"].as_i64().ok_or(ToolsError::Json(
98 "['pages_returned'] is not an integer".into(),
99 ))?;
100 let pages_total = j["pages_total"]
101 .as_i64()
102 .ok_or(ToolsError::Json("['pages_total'] is not an integer".into()))?;
103 if pages_returned != pages_total {
104 return Err(ToolsError::Json(format!(
105 "pages_returned ({}) != pages_total ({})",
106 pages_returned, pages_total
107 )));
108 }
109 if pages_total != self.prefixed_titles.len() as i64 {
110 return Err(ToolsError::Json(format!(
111 "pages_total ({}) != prefixed_titles.len() ({})",
112 pages_total,
113 self.prefixed_titles.len()
114 )));
115 }
116 Ok(())
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_pagepile_new() {
126 let pp = PagePile::new(1);
127 assert_eq!(pp.id, 1);
128 }
129
130 #[cfg(feature = "blocking")]
131 #[test]
132 fn test_pagepile_get_blocking() {
133 let mut pp = PagePile::new(51805);
134 pp.run_blocking().unwrap();
135 assert_eq!(pp.language().unwrap(), "de");
136 assert_eq!(pp.project().unwrap(), "wikipedia");
137 assert_eq!(pp.wiki().unwrap(), "dewiki");
138 assert_eq!(pp.prefixed_titles().len(), 1747);
139 }
140
141 #[cfg(feature = "tokio")]
142 #[tokio::test]
143 async fn test_pagepile_get_async() {
144 let mut pp = PagePile::new(51805);
145 pp.run().await.unwrap();
146 assert_eq!(pp.language().unwrap(), "de");
147 assert_eq!(pp.project().unwrap(), "wikipedia");
148 assert_eq!(pp.wiki().unwrap(), "dewiki");
149 assert_eq!(pp.prefixed_titles().len(), 1747);
150 }
151}