use serde::{de, Deserialize};
use std::collections::HashMap;
use crate::{Error, Result};
#[derive(Debug, Deserialize)]
pub struct Response {
pub query: SiteInfo,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SiteInfo {
pub general: General,
pub namespaces: HashMap<String, NamespaceInfo>,
#[serde(rename = "namespacealiases")]
pub namespace_aliases: Vec<NamespaceAlias>,
#[serde(default)]
#[serde(rename = "interwikimap")]
pub interwiki_map: Vec<Interwiki>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct General {
#[serde(rename = "mainpage")]
pub main_page: String,
pub lang: String,
#[serde(rename = "legaltitlechars")]
pub legal_title_chars: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct NamespaceInfo {
pub id: i32,
pub case: String,
#[serde(alias = "*")]
pub name: String,
pub canonical: Option<String>,
}
impl NamespaceInfo {
pub fn try_from_iter<I: IntoIterator<Item = (String, String)>>(
iter: I,
) -> Result<Self> {
use Error::*;
let mut items: Vec<_> = iter
.into_iter()
.filter(|(k, _)| {
["id", "case", "name", "canonical"].contains(&k.as_str())
})
.collect();
let mut get_string = move |key_to_find| {
items
.iter()
.position(|(k, _)| k == key_to_find)
.map(|pos| items.remove(pos).1)
.ok_or(NamespaceInfoMissingKey(key_to_find))
};
let id = get_string("id")?;
let id = id.parse().map_err(|_| NamespaceInfoInvalidId(id))?;
Ok(NamespaceInfo {
id,
case: get_string("case")?,
name: get_string("name")?,
canonical: get_string("canonical").ok(),
})
}
}
#[test]
fn test_namespace_info_from_iter() {
let (input, expected) = (
[("id", "0"), ("case", "first-letter"), ("name", "")],
NamespaceInfo {
id: 0,
case: "first-letter".into(),
name: "".into(),
canonical: None,
},
);
assert_eq!(
NamespaceInfo::try_from_iter(
input
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
)
.map_err(|e| e.to_string()),
Ok(expected)
);
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
pub struct NamespaceAlias {
pub id: i32,
#[serde(alias = "*")]
pub alias: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Interwiki {
pub prefix: String,
#[serde(default)]
#[serde(rename = "localinterwiki")]
#[serde(deserialize_with = "deserialize_bool_or_string")]
pub local_interwiki: bool,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
enum BooleanCompat {
V2(bool),
#[allow(dead_code)]
V1(String),
}
fn deserialize_bool_or_string<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: de::Deserializer<'de>,
{
let val = BooleanCompat::deserialize(deserializer)?;
Ok(match val {
BooleanCompat::V2(bool) => bool,
BooleanCompat::V1(_) => true,
})
}