#![deny(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
type NamespaceID = crate::api::NamespaceID;
pub fn toggle_namespace_id(id: NamespaceID) -> Option<NamespaceID> {
match id {
n if n >= 0 && n % 2 == 0 => Some(n + 1),
n if n >= 0 && n % 2 == 1 => Some(n - 1),
_ => None,
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Title {
title: String, namespace_id: NamespaceID,
}
impl Title {
pub fn new(title: &str, namespace_id: NamespaceID) -> Title {
Title {
title: Title::underscores_to_spaces(&title),
namespace_id,
}
}
pub fn new_from_full(full_title: &str, api: &crate::api::Api) -> Self {
let mut v: Vec<&str> = full_title.split(':').collect();
if v.len() == 1 {
return Self::new(&full_title, 0);
}
let namespace_name = Title::first_letter_uppercase(&v.remove(0));
let title = Title::underscores_to_spaces(&v.join(":"));
let site_info = api.get_site_info();
if let Some(namespaces) = site_info["query"]["namespaces"].as_object() {
for (_, ns) in namespaces {
if let Some(namespace) = ns["*"].as_str() {
if Title::underscores_to_spaces(&namespace)
== namespace_name
{
return Self::new_from_namespace_object(title, ns);
}
}
if let Some(namespace) = ns["canonical"].as_str() {
if Title::underscores_to_spaces(&namespace)
== namespace_name
{
return Self::new_from_namespace_object(title, ns);
}
}
}
}
if let Some(namespaces) = site_info["query"]["namespacealiases"].as_array() {
for ns in namespaces {
if let Some(namespace) = ns["*"].as_str() {
if Title::underscores_to_spaces(&namespace)
== namespace_name
{
let namespace_id = ns["id"].as_i64().unwrap();
let title = match ns["case"].as_str() {
Some("first-letter") => Title::first_letter_uppercase(&title),
_ => title,
};
return Self::new(&title, namespace_id);
}
}
}
}
Self::new(&full_title, 0)
}
fn new_from_namespace_object(title: String, ns: &serde_json::Value) -> Self {
let namespace_id = ns["id"].as_i64().unwrap();
let title = match ns["case"].as_str() {
Some("first-letter") => Title::first_letter_uppercase(&title),
_ => title,
};
Self::new(&title, namespace_id)
}
pub fn new_from_api_result(data: &serde_json::Value) -> Title {
let namespace_id = data["ns"].as_i64().unwrap_or(0);
let mut title = data["title"].as_str().unwrap_or("").to_string();
if namespace_id > 0 {
let mut v: Vec<&str> = title.split(':').collect();
if v.len() > 1 {
v.remove(0);
title = v.join(":");
}
}
Title {
title: Title::underscores_to_spaces(&title),
namespace_id,
}
}
pub fn namespace_id(&self) -> NamespaceID {
self.namespace_id
}
pub fn namespace_name<'a>(&self, api: &'a crate::api::Api) -> Option<&'a str> {
api.get_canonical_namespace_name(self.namespace_id)
}
pub fn local_namespace_name<'a>(&self, api: &'a crate::api::Api) -> Option<&'a str> {
api.get_local_namespace_name(self.namespace_id)
}
pub fn with_underscores(&self) -> String {
Title::spaces_to_underscores(&self.title)
}
pub fn pretty(&self) -> &str {
&self.title }
pub fn full_with_underscores(&self, api: &crate::api::Api) -> Option<String> {
Some(
match Title::spaces_to_underscores(&self.local_namespace_name(api)?).as_str() {
"" => self.with_underscores(),
ns => ns.to_owned() + ":" + &self.with_underscores(),
},
)
}
pub fn full_pretty(&self, api: &crate::api::Api) -> Option<String> {
Some(
match Title::underscores_to_spaces(&self.local_namespace_name(api)?).as_str() {
"" => self.pretty().to_string(),
ns => ns.to_owned() + ":" + self.pretty(),
},
)
}
pub fn spaces_to_underscores(s: &str) -> String {
s.trim().replace(" ", "_")
}
pub fn underscores_to_spaces(s: &str) -> String {
s.replace("_", " ").trim().to_string()
}
pub fn first_letter_uppercase(s: &str) -> String {
let s = Title::underscores_to_spaces(s);
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
}
pub fn toggle_talk(&mut self) {
self.namespace_id = toggle_namespace_id(self.namespace_id).unwrap_or(self.namespace_id);
}
pub fn into_toggle_talk(self) -> Self {
Title::new(&self.title, toggle_namespace_id(self.namespace_id).unwrap_or(self.namespace_id))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::*;
async fn wd_api() -> Api {
Api::new("https://www.wikidata.org/w/api.php").await.unwrap()
}
#[tokio::test]
async fn new_from_full_main_namespace() {
assert_eq!(
Title::new_from_full(&"Main namespace", &wd_api().await),
Title::new("Main namespace", 0)
);
}
#[tokio::test]
async fn new_from_full_canonical_namespace() {
assert_eq!(
Title::new_from_full(&"File:Some file.jpg", &wd_api().await),
Title::new("Some file.jpg", 6)
);
}
#[tokio::test]
async fn new_from_full_canonical_namespace_with_colon() {
assert_eq!(
Title::new_from_full(&"Project talk:A project:yes, really", &wd_api().await),
Title::new("A project:yes, really", 5)
);
}
#[tokio::test]
async fn new_from_full_namespace_alias() {
assert_eq!(
Title::new_from_full(&"Item:Q12345", &wd_api().await),
Title::new("Q12345", 0)
);
}
#[tokio::test]
async fn new_from_full_special_namespace() {
assert_eq!(
Title::new_from_full(&"Special:A title", &wd_api().await),
Title::new("A title", -1)
);
}
#[tokio::test]
async fn new_from_full_invalid_namespace() {
assert_eq!(
Title::new_from_full(&"This is not a namespace:A title", &wd_api().await),
Title::new("This is not a namespace:A title", 0)
);
}
#[tokio::test]
async fn spaces_to_underscores() {
assert_eq!(
Title::spaces_to_underscores(&" A little test "),
"A_little__test"
);
}
#[tokio::test]
async fn underscores_to_spaces() {
assert_eq!(
Title::underscores_to_spaces(&"_A_little__test_"),
"A little test"
);
}
#[tokio::test]
async fn first_letter_uppercase() {
assert_eq!(Title::first_letter_uppercase(&""), "");
assert_eq!(
Title::first_letter_uppercase(&"FooBar"),
"FooBar"
);
assert_eq!(
Title::first_letter_uppercase(&"fooBar"),
"FooBar"
);
assert_eq!(Title::first_letter_uppercase(&"über"), "Über");
}
#[tokio::test]
async fn full() {
let api = &wd_api().await;
let title = Title::new_from_full(&"User talk:Magnus_Manske", api);
assert_eq!(
title.full_pretty(api),
Some("User talk:Magnus Manske".to_string())
);
assert_eq!(
title.full_with_underscores(api),
Some("User_talk:Magnus_Manske".to_string())
);
}
}