use crate::action_api::{ActionApiData, ActionApiRunnable};
use std::{collections::HashMap, marker::PhantomData};
#[derive(Debug, Clone)]
pub struct ActionApiWbGetEntitiesData {
ids: Option<WbGetEntitiesTitles>,
redirects: bool,
props: Option<Vec<String>>,
languages: Option<Vec<String>>,
languagefallback: bool,
normalize: bool,
sitefilter: Option<Vec<String>>,
}
impl Default for ActionApiWbGetEntitiesData {
fn default() -> Self {
Self {
ids: None,
redirects: true,
props: None,
languages: None,
languagefallback: false,
normalize: false,
sitefilter: None,
}
}
}
impl ActionApiData for ActionApiWbGetEntitiesData {}
impl ActionApiWbGetEntitiesData {
pub(crate) fn params(&self) -> HashMap<String, String> {
let mut params = HashMap::new();
if let Some(ids) = &self.ids {
ids.params(&mut params);
}
if !self.redirects {
params.insert("redirects".to_string(), "no".to_string());
}
Self::add_vec(&self.props, "props", &mut params);
Self::add_vec(&self.languages, "languages", &mut params);
Self::add_vec(&self.sitefilter, "sitefilter", &mut params);
Self::add_boolean(self.languagefallback, "languagefallback", &mut params);
Self::add_boolean(self.normalize, "normalize", &mut params);
params
}
pub(crate) fn with_ids(mut self, ids: WbGetEntitiesTitles) -> Self {
self.ids = Some(ids);
self
}
}
#[derive(Debug, Clone, Copy)]
pub struct NoTitles;
#[derive(Debug, Clone)]
pub enum WbGetEntitiesTitles {
Ids(Vec<String>),
SiteTitles { site: String, titles: Vec<String> },
SitesTitle { sites: Vec<String>, title: String },
}
impl WbGetEntitiesTitles {
pub(crate) fn params(&self, params: &mut HashMap<String, String>) {
match self {
WbGetEntitiesTitles::Ids(ids) => {
params.insert("ids".to_string(), ids.join("|"));
}
WbGetEntitiesTitles::SiteTitles { site, titles } => {
params.insert("sites".to_string(), site.clone());
params.insert("titles".to_string(), titles.join("|"));
}
WbGetEntitiesTitles::SitesTitle { sites, title } => {
params.insert("sites".to_string(), sites.join("|"));
params.insert("titles".to_string(), title.clone());
}
}
}
}
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct ActionApiWbGetEntitiesBuilder<T> {
_phantom: PhantomData<T>,
data: ActionApiWbGetEntitiesData,
}
impl<T> ActionApiWbGetEntitiesBuilder<T> {
pub fn redirects(mut self, redirects: bool) -> Self {
self.data.redirects = redirects;
self
}
pub fn props<S: Into<String> + Clone>(mut self, props: &[S]) -> Self {
self.data.props = Some(
props
.iter()
.map(|s| s.clone().into())
.collect::<Vec<String>>(),
);
self
}
pub fn languages<S: Into<String> + Clone>(mut self, languages: &[S]) -> Self {
self.data.languages = Some(
languages
.iter()
.map(|s| s.clone().into())
.collect::<Vec<String>>(),
);
self
}
pub fn sitefilter<S: Into<String> + Clone>(mut self, sitefilter: &[S]) -> Self {
self.data.sitefilter = Some(
sitefilter
.iter()
.map(|s| s.clone().into())
.collect::<Vec<String>>(),
);
self
}
pub fn languagefallback(mut self, languagefallback: bool) -> Self {
self.data.languagefallback = languagefallback;
self
}
pub fn normalize(mut self, normalize: bool) -> Self {
self.data.normalize = normalize;
self
}
}
impl ActionApiWbGetEntitiesBuilder<NoTitles> {
pub fn new() -> Self {
Self {
data: ActionApiWbGetEntitiesData::default(),
_phantom: PhantomData,
}
}
pub fn ids<S: Into<String> + Clone>(
self,
ids: &[S],
) -> ActionApiWbGetEntitiesBuilder<WbGetEntitiesTitles> {
let ids = ids.iter().map(|s| s.clone().into()).collect();
ActionApiWbGetEntitiesBuilder {
data: self.data.with_ids(WbGetEntitiesTitles::Ids(ids)),
_phantom: PhantomData,
}
}
pub fn sites_title<S, T>(
self,
sites: &[S],
title: T,
) -> ActionApiWbGetEntitiesBuilder<WbGetEntitiesTitles>
where
S: Into<String> + Clone,
T: AsRef<str>,
{
let sites = sites.iter().map(|s| s.clone().into()).collect();
let title = title.as_ref().to_string();
ActionApiWbGetEntitiesBuilder {
data: self
.data
.with_ids(WbGetEntitiesTitles::SitesTitle { sites, title }),
_phantom: PhantomData,
}
}
pub fn site_titles<S, T>(
self,
site: S,
titles: &[T],
) -> ActionApiWbGetEntitiesBuilder<WbGetEntitiesTitles>
where
S: AsRef<str>,
T: Into<String> + Clone,
{
let site = site.as_ref().into();
let titles = titles.iter().map(|s| s.clone().into()).collect();
ActionApiWbGetEntitiesBuilder {
data: self
.data
.with_ids(WbGetEntitiesTitles::SiteTitles { site, titles }),
_phantom: PhantomData,
}
}
}
impl ActionApiRunnable for ActionApiWbGetEntitiesBuilder<WbGetEntitiesTitles> {
fn params(&self) -> HashMap<String, String> {
let mut ret = self.data.params();
ret.insert("action".to_string(), "wbgetentities".to_string());
ret
}
}
#[cfg(test)]
mod tests {
use crate::{Api, action_api::ActionApi};
use super::*;
fn new_builder() -> ActionApiWbGetEntitiesBuilder<NoTitles> {
ActionApiWbGetEntitiesBuilder::new()
}
#[test]
fn default_redirects_is_true() {
let params = new_builder().ids(&["Q1"]).data.params();
assert!(!params.contains_key("redirects"));
}
#[test]
fn default_props_is_empty() {
let params = new_builder().ids(&["Q1"]).data.params();
assert!(!params.contains_key("props"));
}
#[test]
fn default_languages_is_empty() {
let params = new_builder().ids(&["Q1"]).data.params();
assert!(!params.contains_key("languages"));
}
#[test]
fn default_sitefilter_is_empty() {
let params = new_builder().ids(&["Q1"]).data.params();
assert!(!params.contains_key("sitefilter"));
}
#[test]
fn ids_single() {
let params = new_builder().ids(&["Q42"]).data.params();
assert_eq!(params["ids"], "Q42");
}
#[test]
fn ids_multiple() {
let params = new_builder().ids(&["Q1", "Q2", "Q3"]).data.params();
assert_eq!(params["ids"], "Q1|Q2|Q3");
}
#[test]
fn ids_does_not_set_sites_or_titles() {
let params = new_builder().ids(&["Q42"]).data.params();
assert!(!params.contains_key("sites"));
assert!(!params.contains_key("titles"));
}
#[test]
fn sites_title_single_site() {
let params = new_builder()
.sites_title(&["enwiki"], "Douglas Adams")
.data
.params();
assert_eq!(params["sites"], "enwiki");
assert_eq!(params["titles"], "Douglas Adams");
}
#[test]
fn sites_title_multiple_sites() {
let params = new_builder()
.sites_title(&["enwiki", "dewiki"], "Berlin")
.data
.params();
assert_eq!(params["sites"], "enwiki|dewiki");
assert_eq!(params["titles"], "Berlin");
}
#[test]
fn sites_title_does_not_set_ids() {
let params = new_builder().sites_title(&["enwiki"], "Foo").data.params();
assert!(!params.contains_key("ids"));
}
#[test]
fn site_titles_single_title() {
let params = new_builder()
.site_titles("enwiki", &["London"])
.data
.params();
assert_eq!(params["sites"], "enwiki");
assert_eq!(params["titles"], "London");
}
#[test]
fn site_titles_multiple_titles() {
let params = new_builder()
.site_titles("enwiki", &["London", "Paris", "Berlin"])
.data
.params();
assert_eq!(params["sites"], "enwiki");
assert_eq!(params["titles"], "London|Paris|Berlin");
}
#[test]
fn site_titles_does_not_set_ids() {
let params = new_builder().site_titles("enwiki", &["Foo"]).data.params();
assert!(!params.contains_key("ids"));
}
#[test]
fn redirects_false() {
let params = new_builder().redirects(false).ids(&["Q1"]).data.params();
assert_eq!(params["redirects"], "no");
}
#[test]
fn redirects_true_explicit() {
let params = new_builder().redirects(true).ids(&["Q1"]).data.params();
assert!(!params.contains_key("redirects"));
}
#[test]
fn props_single() {
let params = new_builder().props(&["labels"]).ids(&["Q1"]).data.params();
assert_eq!(params["props"], "labels");
}
#[test]
fn props_multiple() {
let params = new_builder()
.props(&["labels", "descriptions", "aliases"])
.ids(&["Q1"])
.data
.params();
assert_eq!(params["props"], "labels|descriptions|aliases");
}
#[test]
fn props_accepts_owned_strings() {
let props = vec!["labels".to_string(), "claims".to_string()];
let params = new_builder().props(&props).ids(&["Q1"]).data.params();
assert_eq!(params["props"], "labels|claims");
}
#[test]
fn languages_single() {
let params = new_builder().languages(&["en"]).ids(&["Q1"]).data.params();
assert_eq!(params["languages"], "en");
}
#[test]
fn languages_multiple() {
let params = new_builder()
.languages(&["en", "de", "fr"])
.ids(&["Q1"])
.data
.params();
assert_eq!(params["languages"], "en|de|fr");
}
#[test]
fn sitefilter_single() {
let params = new_builder()
.sitefilter(&["enwiki"])
.ids(&["Q1"])
.data
.params();
assert_eq!(params["sitefilter"], "enwiki");
}
#[test]
fn sitefilter_multiple() {
let params = new_builder()
.sitefilter(&["enwiki", "dewiki", "frwiki"])
.ids(&["Q1"])
.data
.params();
assert_eq!(params["sitefilter"], "enwiki|dewiki|frwiki");
}
#[test]
fn languagefallback_stored() {
let builder = new_builder().languagefallback(true);
assert!(builder.data.languagefallback);
}
#[test]
fn languagefallback_false_stored() {
let builder = new_builder().languagefallback(false);
assert!(!builder.data.languagefallback);
}
#[test]
fn normalize_stored() {
let builder = new_builder().normalize(true);
assert!(builder.data.normalize);
}
#[test]
fn normalize_false_stored() {
let builder = new_builder().normalize(false);
assert!(!builder.data.normalize);
}
#[test]
fn wbgetentitiestitle_ids_params() {
let mut params = HashMap::new();
WbGetEntitiesTitles::Ids(vec!["Q1".to_string(), "Q2".to_string()]).params(&mut params);
assert_eq!(params["ids"], "Q1|Q2");
assert!(!params.contains_key("sites"));
assert!(!params.contains_key("titles"));
}
#[test]
fn wbgetentitiestitle_site_titles_params() {
let mut params = HashMap::new();
WbGetEntitiesTitles::SiteTitles {
site: "enwiki".to_string(),
titles: vec!["Foo".to_string(), "Bar".to_string()],
}
.params(&mut params);
assert_eq!(params["sites"], "enwiki");
assert_eq!(params["titles"], "Foo|Bar");
assert!(!params.contains_key("ids"));
}
#[test]
fn wbgetentitiestitle_sites_title_params() {
let mut params = HashMap::new();
WbGetEntitiesTitles::SitesTitle {
sites: vec!["enwiki".to_string(), "dewiki".to_string()],
title: "Baz".to_string(),
}
.params(&mut params);
assert_eq!(params["sites"], "enwiki|dewiki");
assert_eq!(params["titles"], "Baz");
assert!(!params.contains_key("ids"));
}
#[test]
fn chaining_multiple_options() {
let params = new_builder()
.redirects(false)
.props(&["labels", "descriptions"])
.languages(&["en", "de"])
.sitefilter(&["enwiki"])
.ids(&["Q42"])
.data
.params();
assert_eq!(params["redirects"], "no");
assert_eq!(params["props"], "labels|descriptions");
assert_eq!(params["languages"], "en|de");
assert_eq!(params["sitefilter"], "enwiki");
assert_eq!(params["ids"], "Q42");
}
#[tokio::test]
async fn test_wbgetentities() {
use wiremock::matchers::query_param;
use wiremock::{Mock, ResponseTemplate};
let server = crate::test_helpers::test_helpers_mod::start_wikidata_mock().await;
Mock::given(query_param("action", "wbgetentities"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"entities": {
"Q5": {
"type": "item", "id": "Q5",
"labels": {"en": {"language": "en", "value": "human"}},
"descriptions": {},
"aliases": {},
"claims": {},
"sitelinks": {}
}
},
"success": 1
})))
.mount(&server)
.await;
let api = Api::new(&server.uri()).await.unwrap();
let result = ActionApi::wbgetentities()
.ids(&["Q5"])
.run(&api)
.await
.unwrap();
assert!(result["entities"]["Q5"].is_object())
}
}