#![deny(
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
extern crate curl;
extern crate mediawiki;
extern crate regex;
extern crate serde;
#[macro_use]
extern crate serde_json;
pub mod entity_container;
pub mod entity_diff;
pub mod from_json;
pub mod query;
pub mod requests;
pub mod validate;
use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize};
use query::{EntityQuery, SearchQuery};
use std::str::FromStr;
use std::{error::Error, fmt};
const USER_AGENT_BASE: &'static str = concat!("Wikibase-RS/", env!("CARGO_PKG_VERSION"));
#[derive(Debug, Clone)]
pub enum WikibaseError {
Configuration(String),
Request(String),
Serialization(String),
Validation(String),
}
impl Error for WikibaseError {}
impl fmt::Display for WikibaseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug, Clone)]
pub struct Configuration {
api_url: String,
user_agent_prefix: String,
api: mediawiki::api::Api,
}
impl Configuration {
pub fn new(user_agent_prefix: &str) -> Result<Configuration, WikibaseError> {
let valid_user_agent_prefix = validate::validate_user_agent(&user_agent_prefix)?;
let default_api_url = "https://www.wikidata.org/w/api.php";
let api = match mediawiki::api::Api::new(default_api_url) {
Ok(api) => api,
_ => {
return Err(WikibaseError::Configuration(
"Can't create mediawiki API object".to_string(),
));
}
};
let mut ret = Self {
api_url: default_api_url.to_string(),
user_agent_prefix: valid_user_agent_prefix.to_string(),
api: api,
};
let ua = ret.user_agent();
ret.api_mut().set_user_agent(ua);
Ok(ret)
}
pub fn api_mut(&mut self) -> &mut mediawiki::api::Api {
&mut self.api
}
pub fn api(&self) -> &mediawiki::api::Api {
&self.api
}
pub fn api_url(&self) -> &str {
&self.api_url
}
pub fn set_api_url<S: Into<String>>(&mut self, api_url: S) {
let new_api_url = api_url.into();
if self.api_url == new_api_url {
return;
}
self.api_url = new_api_url;
self.api = mediawiki::api::Api::new(&self.api_url).unwrap();
let ua = self.user_agent();
self.api.set_user_agent(ua);
}
pub fn user_agent(&self) -> String {
format!("{} {}", &self.user_agent_prefix, USER_AGENT_BASE)
}
pub fn user_agent_prefix(&self) -> &str {
&self.user_agent_prefix
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Coordinate(Coordinate),
MonoLingual(MonoLingualText),
Entity(EntityValue),
Quantity(QuantityValue),
StringValue(String),
Time(TimeValue),
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Value::Coordinate(v) => serializer.serialize_some(&v),
Value::MonoLingual(v) => serializer.serialize_some(&v),
Value::Entity(v) => serializer.serialize_some(&v),
Value::Quantity(v) => serializer.serialize_some(&v),
Value::StringValue(v) => serializer.serialize_some(&v),
Value::Time(v) => serializer.serialize_some(&v),
}
}
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum DataValueType {
EntityId,
GlobeCoordinate,
MonoLingualText,
Quantity,
StringType,
Time,
}
impl DataValueType {
pub fn new_from_str(string_value: &str) -> Result<DataValueType, WikibaseError> {
match string_value {
"globecoordinate" => Ok(DataValueType::GlobeCoordinate),
"monolingualtext" => Ok(DataValueType::MonoLingualText),
"quantity" => Ok(DataValueType::Quantity),
"string" => Ok(DataValueType::StringType),
"wikibase-entityid" => Ok(DataValueType::EntityId),
"time" => Ok(DataValueType::Time),
_ => Err(WikibaseError::Serialization(
"Data value type could not be matched".to_string(),
)),
}
}
pub fn string_value(&self) -> String {
match *self {
DataValueType::EntityId => "wikibase-entityid".to_string(),
DataValueType::GlobeCoordinate => "globecoordinate".to_string(),
DataValueType::MonoLingualText => "monolingualtext".to_string(),
DataValueType::Quantity => "quantity".to_string(),
DataValueType::StringType => "string".to_string(),
DataValueType::Time => "time".to_string(),
}
}
}
impl Serialize for DataValueType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let str_ptr = self.string_value();
serializer.serialize_str(&str_ptr)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Coordinate {
altitude: Option<f64>,
globe: String,
latitude: f64,
longitude: f64,
precision: Option<f64>,
}
impl Coordinate {
fn new_from_json(
object: &serde_json::Map<String, serde_json::Value>,
) -> Result<Coordinate, WikibaseError> {
let mut altitude = None;
let mut precision = None;
if object["altitude"].is_null() == false {
let altitude_string = match object["altitude"].as_str() {
Some(value) => value,
None => return Err(WikibaseError::Serialization("Altitude".to_string())),
};
let altitude_number: f64 = match altitude_string.parse() {
Ok(value) => value,
Err(error) => {
return Err(WikibaseError::Serialization(
error.description().to_string(),
));
}
};
altitude = Some(altitude_number);
}
let latitude = match object["latitude"].as_f64() {
Some(value) => value,
None => return Err(WikibaseError::Serialization("Latitude".to_string())),
};
let longitude = match object["longitude"].as_f64() {
Some(value) => value,
None => return Err(WikibaseError::Serialization("Longitude".to_string())),
};
if object["precision"].is_null() == false {
precision = match object["precision"].as_f64() {
Some(value) => Some(value),
None => return Err(WikibaseError::Serialization("Precision".to_string())),
};
}
let globe = match object["globe"].as_str() {
Some(value) => value,
None => return Err(WikibaseError::Serialization("Globe".to_string())),
};
Ok(Self {
altitude,
globe: globe.to_string(),
latitude,
longitude,
precision,
})
}
pub fn new(
altitude: Option<f64>,
globe: String,
latitude: f64,
longitude: f64,
precision: Option<f64>,
) -> Coordinate {
Self {
altitude,
globe,
latitude,
longitude,
precision,
}
}
pub fn altitude(&self) -> &Option<f64> {
&self.altitude
}
pub fn globe(&self) -> &str {
&self.globe
}
pub fn latitude(&self) -> &f64 {
&self.latitude
}
pub fn longitude(&self) -> &f64 {
&self.longitude
}
pub fn precision(&self) -> &Option<f64> {
&self.precision
}
pub fn set_altitude(&mut self, altitude: Option<f64>) {
self.altitude = altitude;
}
pub fn set_globe<S: Into<String>>(&mut self, globe: S) {
self.globe = globe.into();
}
pub fn set_latitude(&mut self, latitude: f64) {
self.latitude = latitude;
}
pub fn set_longitude(&mut self, longitude: f64) {
self.longitude = longitude;
}
pub fn set_precision(&mut self, precision: Option<f64>) {
self.precision = precision;
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MonoLingualText {
language: String,
text: String,
}
impl MonoLingualText {
fn new_from_json(
object: &serde_json::Map<String, serde_json::Value>,
) -> Result<MonoLingualText, WikibaseError> {
let language = match object["language"].as_str() {
Some(value) => value,
None => return Err(WikibaseError::Serialization("Language".to_string())),
};
let text = match object["text"].as_str() {
Some(value) => value,
None => return Err(WikibaseError::Serialization("Text".to_string())),
};
Ok(Self {
language: language.to_string(),
text: text.to_string(),
})
}
pub fn new<S: Into<String>>(text: S, language: S) -> MonoLingualText {
Self {
text: text.into(),
language: language.into(),
}
}
pub fn language(&self) -> &str {
&self.language
}
pub fn set_language<S: Into<String>>(&mut self, language: S) {
self.language = language.into();
}
pub fn set_text<S: Into<String>>(&mut self, text: S) {
self.text = text.into();
}
pub fn text(&self) -> &str {
&self.text
}
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum EntityType {
Item,
Property,
}
impl EntityType {
pub fn new_from_str(type_string: &str) -> Option<EntityType> {
match type_string {
"item" => Some(EntityType::Item),
"property" => Some(EntityType::Property),
_ => None,
}
}
pub fn new_from_id(id_string: &str) -> Result<EntityType, WikibaseError> {
let first_char = match id_string.chars().nth(0) {
Some(value) => value,
None => {
return Err(WikibaseError::Validation(
"Error getting first character of string".to_string(),
));
}
};
match first_char {
'P' => Ok(EntityType::Property),
'Q' => Ok(EntityType::Item),
_ => Err(WikibaseError::Serialization(
"Error matching entity type".to_string(),
)),
}
}
pub fn string_value(&self) -> String {
match *self {
EntityType::Item => "item".to_string(),
EntityType::Property => "property".to_string(),
}
}
}
impl Serialize for EntityType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let str_ptr = self.string_value();
serializer.serialize_str(&str_ptr)
}
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum StatementRank {
Deprecated,
Normal,
Preferred,
}
impl StatementRank {
pub fn as_str(&self) -> &str {
match self {
StatementRank::Deprecated => "deprecated",
StatementRank::Normal => "normal",
StatementRank::Preferred => "preferred",
}
}
}
#[derive(Debug, Clone)]
pub struct Statement {
claim_type: String,
rank: StatementRank,
main_snak: Snak,
qualifiers: Vec<Snak>,
references: Vec<Reference>,
id: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DataValue {
value: Value,
value_type: DataValueType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocaleString {
language: String,
value: String,
}
impl PartialEq for LocaleString {
fn eq(&self, other: &LocaleString) -> bool {
self.language == other.language && self.value == other.value
}
}
impl Eq for LocaleString {}
trait EntityTrait {
fn new(
id: String,
labels: Vec<LocaleString>,
descriptions: Vec<LocaleString>,
aliases: Vec<LocaleString>,
claims: Vec<Statement>,
sitelinks: Option<Vec<SiteLink>>,
missing: bool,
) -> Self;
fn new_empty() -> Self;
}
#[derive(Debug, Clone)]
pub struct ItemEntity {
id: String,
labels: Vec<LocaleString>,
descriptions: Vec<LocaleString>,
aliases: Vec<LocaleString>,
claims: Vec<Statement>,
sitelinks: Option<Vec<SiteLink>>,
missing: bool,
entity_type: EntityType,
}
#[derive(Debug, Clone)]
pub struct PropertyEntity {
id: String,
labels: Vec<LocaleString>,
descriptions: Vec<LocaleString>,
aliases: Vec<LocaleString>,
claims: Vec<Statement>,
sitelinks: Option<Vec<SiteLink>>,
missing: bool,
entity_type: EntityType,
}
impl EntityTrait for ItemEntity {
fn new(
id: String,
labels: Vec<LocaleString>,
descriptions: Vec<LocaleString>,
aliases: Vec<LocaleString>,
claims: Vec<Statement>,
sitelinks: Option<Vec<SiteLink>>,
missing: bool,
) -> Self {
Self {
id,
labels,
descriptions,
aliases,
claims,
sitelinks,
missing,
entity_type: EntityType::Item,
}
}
fn new_empty() -> Self {
Self {
id: "".to_string(),
labels: vec![],
descriptions: vec![],
aliases: vec![],
claims: vec![],
sitelinks: None,
missing: false,
entity_type: EntityType::Item,
}
}
}
impl EntityTrait for PropertyEntity {
fn new(
id: String,
labels: Vec<LocaleString>,
descriptions: Vec<LocaleString>,
aliases: Vec<LocaleString>,
claims: Vec<Statement>,
sitelinks: Option<Vec<SiteLink>>,
missing: bool,
) -> Self {
Self {
id,
labels,
descriptions,
aliases,
claims,
sitelinks,
missing,
entity_type: EntityType::Property,
}
}
fn new_empty() -> Self {
Self {
id: "".to_string(),
labels: vec![],
descriptions: vec![],
aliases: vec![],
claims: vec![],
sitelinks: None,
missing: false,
entity_type: EntityType::Property,
}
}
}
impl ItemEntity {
pub fn set_sitelink(&mut self, new_sitelink: SiteLink) {
match self.sitelinks {
None => {
self.sitelinks = Some(vec![new_sitelink]);
return;
}
Some(ref mut sitelinks) => {
for num in 0..sitelinks.len() {
if sitelinks[num].site() == new_sitelink.site() {
sitelinks[num] = new_sitelink;
return;
}
}
sitelinks.push(new_sitelink);
}
}
}
}
impl PropertyEntity {
pub fn set_sitelink(&mut self, new_sitelink: SiteLink) {
match self.sitelinks {
None => {
self.sitelinks = Some(vec![new_sitelink]);
return;
}
Some(ref mut sitelinks) => {
for num in 0..sitelinks.len() {
if sitelinks[num].site() == new_sitelink.site() {
sitelinks[num] = new_sitelink;
return;
}
}
sitelinks.push(new_sitelink);
}
}
}
}
#[derive(Debug, Clone)]
pub enum Entity {
Item(ItemEntity),
Property(PropertyEntity),
}
impl Entity {
pub fn new_empty_item() -> Self {
Entity::Item(ItemEntity::new_empty())
}
pub fn new_empty_property() -> Self {
Entity::Property(PropertyEntity::new_empty())
}
pub fn new_item(
id: String,
labels: Vec<LocaleString>,
descriptions: Vec<LocaleString>,
aliases: Vec<LocaleString>,
claims: Vec<Statement>,
sitelinks: Option<Vec<SiteLink>>,
missing: bool,
) -> Self {
Entity::Item(ItemEntity::new(
id,
labels,
descriptions,
aliases,
claims,
sitelinks,
missing,
))
}
pub fn new_property(
id: String,
labels: Vec<LocaleString>,
descriptions: Vec<LocaleString>,
aliases: Vec<LocaleString>,
claims: Vec<Statement>,
sitelinks: Option<Vec<SiteLink>>,
missing: bool,
) -> Self {
Entity::Property(PropertyEntity::new(
id,
labels,
descriptions,
aliases,
claims,
sitelinks,
missing,
))
}
pub fn new_from_id<S: Into<String>>(
id: S,
configuration: &Configuration,
) -> Result<Entity, WikibaseError> {
let ids = vec![id.into()];
let query = EntityQuery::new(ids, "en");
Entity::new_from_query(&query, &configuration)
}
pub fn new_from_ids(
ids: Vec<String>,
configuration: &Configuration,
) -> Result<Entity, WikibaseError> {
let query = EntityQuery::new(ids, "en");
Entity::new_from_query(&query, &configuration)
}
pub fn new_from_query(
query: &EntityQuery,
configuration: &Configuration,
) -> Result<Entity, WikibaseError> {
let params = query.params();
let json_response = match configuration.api().get_query_api_json_all(¶ms) {
Ok(j) => j,
Err(_) => return Err(WikibaseError::Configuration("Query failed".to_string())),
};
let id = &query.ids()[0];
let json_entity = &json_response["entities"][id];
from_json::entity_from_json(json_entity)
}
pub fn aliases(&self) -> &Vec<LocaleString> {
match self {
Entity::Item(e) => &e.aliases,
Entity::Property(e) => &e.aliases,
}
}
pub fn descriptions(&self) -> &Vec<LocaleString> {
match self {
Entity::Item(e) => &e.descriptions,
Entity::Property(e) => &e.descriptions,
}
}
pub fn labels(&self) -> &Vec<LocaleString> {
match self {
Entity::Item(e) => &e.labels,
Entity::Property(e) => &e.labels,
}
}
pub fn label_in_locale(&self, locale: &str) -> Option<&str> {
for label in self.labels().iter() {
if label.language() == locale {
return Some(label.value());
}
}
None
}
pub fn description_in_locale(&self, locale: &str) -> Option<&str> {
let descriptions = match self {
Entity::Item(e) => &e.descriptions,
Entity::Property(e) => &e.descriptions,
};
for description in descriptions.iter() {
if description.language() == locale {
return Some(description.value());
}
}
None
}
pub fn set_aliases(&mut self, aliases: Vec<LocaleString>) {
match self {
Entity::Item(e) => e.aliases = aliases,
Entity::Property(e) => e.aliases = aliases,
};
}
pub fn set_claims(&mut self, claims: Vec<Statement>) {
match self {
Entity::Item(e) => e.claims = claims,
Entity::Property(e) => e.claims = claims,
};
}
pub fn add_claim(&mut self, claim: Statement) {
match self {
Entity::Item(e) => e.claims.push(claim),
Entity::Property(e) => e.claims.push(claim),
};
}
pub fn set_descriptions(&mut self, descriptions: Vec<LocaleString>) {
match self {
Entity::Item(e) => e.descriptions = descriptions,
Entity::Property(e) => e.descriptions = descriptions,
};
}
pub fn set_id(&mut self, id: String) {
match self {
Entity::Item(e) => e.id = id,
Entity::Property(e) => e.id = id,
};
}
pub fn set_labels(&mut self, labels: Vec<LocaleString>) {
match self {
Entity::Item(e) => e.labels = labels,
Entity::Property(e) => e.labels = labels,
};
}
pub fn set_missing(&mut self, missing: bool) {
match self {
Entity::Item(e) => e.missing = missing,
Entity::Property(e) => e.missing = missing,
};
}
pub fn set_sitelinks(&mut self, sitelinks: Option<Vec<SiteLink>>) {
match self {
Entity::Item(e) => e.sitelinks = sitelinks,
Entity::Property(e) => e.sitelinks = sitelinks,
};
}
pub fn set_label(&mut self, new_label: LocaleString) {
let labels = match self {
Entity::Item(e) => &mut e.labels,
Entity::Property(e) => &mut e.labels,
};
for num in 0..labels.len() {
if labels[num].language() == new_label.language() {
labels[num].set_value(new_label.value());
return;
}
}
labels.push(new_label);
}
pub fn remove_label<S: Into<String>>(&mut self, language: S) {
let labels = match self {
Entity::Item(e) => &mut e.labels,
Entity::Property(e) => &mut e.labels,
};
let language: String = language.into();
labels.retain(|x| x.language != language);
}
pub fn remove_description<S: Into<String>>(&mut self, language: S) {
let descriptions = match self {
Entity::Item(e) => &mut e.descriptions,
Entity::Property(e) => &mut e.descriptions,
};
let language: String = language.into();
descriptions.retain(|x| x.language != language);
}
pub fn set_sitelink(&mut self, new_sitelink: SiteLink) {
match self {
Entity::Item(e) => e.set_sitelink(new_sitelink),
Entity::Property(e) => e.set_sitelink(new_sitelink),
};
}
pub fn remove_sitelink<S: Into<String>>(&mut self, site: S) {
let sitelinks = match self {
Entity::Item(e) => &mut e.sitelinks,
Entity::Property(e) => &mut e.sitelinks,
};
let site: String = site.into();
match sitelinks {
Some(sitelinks) => sitelinks.retain(|x| x.site != site),
None => {}
}
}
pub fn set_description(&mut self, new_description: LocaleString) {
let descriptions = match self {
Entity::Item(e) => &mut e.descriptions,
Entity::Property(e) => &mut e.descriptions,
};
for num in 0..descriptions.len() {
if descriptions[num].language() == new_description.language() {
descriptions[num].set_value(new_description.value());
return;
}
}
descriptions.push(new_description);
}
pub fn add_alias(&mut self, new_alias: LocaleString) {
if new_alias.value.is_empty() {
return;
}
let aliases = match self {
Entity::Item(e) => &mut e.aliases,
Entity::Property(e) => &mut e.aliases,
};
if !aliases.contains(&new_alias) {
aliases.push(new_alias);
}
}
pub fn remove_alias(&mut self, alias: LocaleString) {
let aliases = match self {
Entity::Item(e) => &mut e.aliases,
Entity::Property(e) => &mut e.aliases,
};
aliases.retain(|x| *x != alias);
}
pub fn sitelinks(&self) -> &Option<Vec<SiteLink>> {
match self {
Entity::Item(e) => &e.sitelinks,
Entity::Property(e) => &e.sitelinks,
}
}
pub fn claims(&self) -> &Vec<Statement> {
match self {
Entity::Item(e) => &e.claims,
Entity::Property(e) => &e.claims,
}
}
pub fn claims_mut(&mut self) -> &mut Vec<Statement> {
match self {
Entity::Item(e) => &mut e.claims,
Entity::Property(e) => &mut e.claims,
}
}
pub fn id(&self) -> &str {
match self {
Entity::Item(e) => &e.id,
Entity::Property(e) => &e.id,
}
}
pub fn missing(&self) -> bool {
match self {
Entity::Item(e) => e.missing,
Entity::Property(e) => e.missing,
}
}
pub fn claims_with_property<S: Into<String>>(&self, property_id: S) -> Vec<&Statement> {
let property_id: String = property_id.into();
self.claims()
.iter()
.filter(|claim| claim.main_snak().property() == property_id)
.collect()
}
pub fn values_for_property<S: Into<String>>(&self, property_id: S) -> Vec<Value> {
self.claims_with_property(property_id)
.iter()
.map(|c| c.main_snak().data_value())
.filter(|dv| dv.is_some())
.map(|dv| dv.to_owned().unwrap().to_owned().value().to_owned())
.collect()
}
pub fn has_target_entity<S: Into<String>>(&self, property_id: S, target_entity_id: S) -> bool {
let target_entity_id: String = target_entity_id.into();
self.values_for_property(property_id)
.iter()
.filter(|v| match v {
Value::Entity(target_q) => (target_q.id() == target_entity_id),
_ => false,
})
.count()
> 0
}
pub fn claim_with_id<S: Into<String>>(&self, id: S) -> Option<&Statement> {
let id: &str = &id.into();
self.claims()
.iter()
.filter(|claim| match claim.id() {
Some(x) => x == id,
None => false,
})
.next()
}
pub fn has_claims_with_property<S: Into<String>>(&self, property_id: S) -> bool {
let property_id: String = property_id.into();
match self
.claims()
.iter()
.filter(|claim| claim.main_snak().property() == property_id)
.nth(0)
{
Some(_) => true,
None => false,
}
}
pub fn entity_type(&self) -> &EntityType {
match self {
Entity::Item(e) => &e.entity_type,
Entity::Property(e) => &e.entity_type,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SiteLink {
badges: Vec<String>,
site: String,
title: String,
}
impl SiteLink {
pub fn new<S: Into<String>>(site: S, title: S, badges: Vec<String>) -> SiteLink {
let mut ret = Self {
badges,
site: site.into(),
title: title.into(),
};
ret.badges.sort();
ret
}
pub fn site(&self) -> &String {
&self.site
}
pub fn title(&self) -> &String {
&self.title
}
pub fn badges(&self) -> &Vec<String> {
&self.badges
}
}
impl PartialEq for SiteLink {
fn eq(&self, other: &SiteLink) -> bool {
self.site == other.site && self.title == other.title && self.badges == other.badges
}
}
impl Eq for SiteLink {}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct QuantityValue {
amount: f64,
lower_bound: Option<f64>,
unit: String,
upper_bound: Option<f64>,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct Reference {
snaks: Vec<Snak>,
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum SnakType {
NoValue,
UnknownValue,
Value,
}
impl SnakType {
pub fn string_mapping(&self) -> &str {
match self {
&SnakType::Value => &"Value",
&SnakType::NoValue => &"No Value",
&SnakType::UnknownValue => &"Some Value",
}
}
}
impl Serialize for SnakType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
&SnakType::Value => serializer.serialize_str("value"),
&SnakType::NoValue => serializer.serialize_str("novalue"),
&SnakType::UnknownValue => serializer.serialize_str("somevalue"),
}
}
}
#[derive(Debug, Clone)]
pub struct SearchResultEntity {
id: String,
entity_type: EntityType,
label: LocaleString,
description: Option<LocaleString>,
aliases: Vec<LocaleString>,
}
impl SearchResultEntity {
pub fn new<S: Into<String>>(
id: S,
entity_type: EntityType,
label: LocaleString,
description: Option<LocaleString>,
aliases: Vec<LocaleString>,
) -> SearchResultEntity {
Self {
id: id.into(),
entity_type,
label,
description,
aliases,
}
}
pub fn aliases(&self) -> &Vec<LocaleString> {
&self.aliases
}
pub fn description(&self) -> &Option<LocaleString> {
&self.description
}
pub fn id(&self) -> &str {
&self.id
}
pub fn label(&self) -> &LocaleString {
&self.label
}
pub fn set_aliases(&mut self, aliases: Vec<LocaleString>) {
self.aliases = aliases;
}
pub fn set_descriptions(&mut self, description: Option<LocaleString>) {
self.description = description;
}
pub fn set_labels(&mut self, label: LocaleString) {
self.label = label;
}
}
#[derive(Debug, Clone)]
pub struct SearchResults {
results: Vec<SearchResultEntity>,
}
impl SearchResults {
pub fn new(results: Vec<SearchResultEntity>) -> SearchResults {
Self { results }
}
pub fn new_from_query(
query: &SearchQuery,
configuration: &Configuration,
) -> Result<SearchResults, WikibaseError> {
let params = query.params();
let request_result = configuration.api().get_query_api_json_all(¶ms);
let json_response = match request_result {
Ok(value) => value,
Err(_error) => return Err(WikibaseError::Configuration("Search failed".to_string())),
};
from_json::search_result_entities_from_json(&json_response, &query)
}
pub fn results(&self) -> &Vec<SearchResultEntity> {
&self.results
}
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum SnakDataType {
Time,
WikibaseItem,
String,
ExternalId,
GlobeCoordinate,
MonolingualText,
Quantity,
Value,
NoValue,
SomeValue,
}
impl FromStr for SnakDataType {
type Err = WikibaseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"time" => Ok(SnakDataType::String),
"wikibase-item" => Ok(SnakDataType::WikibaseItem),
"external-id" => Ok(SnakDataType::ExternalId),
"string" => Ok(SnakDataType::String),
"globe-coordinate" => Ok(SnakDataType::GlobeCoordinate),
"monolingualtext" => Ok(SnakDataType::MonolingualText),
"quantity" => Ok(SnakDataType::Quantity),
"value" => Ok(SnakDataType::Value),
"novalue" => Ok(SnakDataType::NoValue),
"somevalue" => Ok(SnakDataType::SomeValue),
_ => Err(WikibaseError::Serialization(format!(
"SnakDataType parsing error: {}",
s
))),
}
}
}
impl Serialize for SnakDataType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
SnakDataType::Time => serializer.serialize_str(&"time"),
SnakDataType::WikibaseItem => serializer.serialize_str(&"wikibase-item"),
SnakDataType::ExternalId => serializer.serialize_str(&"external-id"),
SnakDataType::String => serializer.serialize_str(&"string"),
SnakDataType::GlobeCoordinate => serializer.serialize_str(&"globe-coordinate"),
SnakDataType::MonolingualText => serializer.serialize_str(&"monolingualtext"),
SnakDataType::Quantity => serializer.serialize_str(&"quantity"),
SnakDataType::Value => serializer.serialize_str(&"value"),
SnakDataType::NoValue => serializer.serialize_str(&"novalue"),
SnakDataType::SomeValue => serializer.serialize_str(&"somevalue"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Snak {
datatype: SnakDataType,
property: String,
snak_type: SnakType,
data_value: Option<DataValue>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EntityValue {
entity_type: EntityType,
id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TimeValue {
after: u64,
before: u64,
calendarmodel: String,
precision: u64,
time: String,
timezone: u64,
}
impl LocaleString {
pub fn new<S: Into<String>>(language: S, value: S) -> LocaleString {
Self {
language: language.into(),
value: value.into(),
}
}
pub fn language(&self) -> &str {
&self.language
}
pub fn value(&self) -> &str {
&self.value
}
pub fn set_value(&mut self, value: &str) {
self.value = value.into();
}
}
impl QuantityValue {
fn new_from_object(
value: &serde_json::Map<String, serde_json::Value>,
) -> Result<QuantityValue, WikibaseError> {
let amount = match from_json::float_from_json(&value, "amount") {
Some(value) => value,
None => return Err(WikibaseError::Serialization("Amount".to_string())),
};
let lower_bound = from_json::float_from_json(&value, "lowerBound");
let upper_bound = from_json::float_from_json(&value, "upperBound");
Ok(Self {
amount,
lower_bound,
unit: value["unit"].as_str().unwrap_or_else(|| "").to_string(),
upper_bound,
})
}
pub fn new<S: Into<String>>(
amount: f64,
lower_bound: Option<f64>,
unit: S,
upper_bound: Option<f64>,
) -> QuantityValue {
Self {
amount,
lower_bound,
unit: unit.into(),
upper_bound,
}
}
pub fn amount(&self) -> &f64 {
&self.amount
}
pub fn lower_bound(&self) -> &Option<f64> {
&self.lower_bound
}
pub fn set_amount(&mut self, amount: f64) {
self.amount = amount;
}
pub fn set_lower_bound(&mut self, lower_bound: Option<f64>) {
self.lower_bound = lower_bound;
}
pub fn set_unit<S: Into<String>>(&mut self, unit: S) {
self.unit = unit.into();
}
pub fn set_upper_bound(&mut self, upper_bound: Option<f64>) {
self.upper_bound = upper_bound;
}
pub fn unit(&self) -> &str {
&self.unit
}
pub fn upper_bound(&self) -> &Option<f64> {
&self.upper_bound
}
}
impl Serialize for Entity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if self.missing() {
return serializer.serialize_none();
}
let mut number_of_fields = 5;
let entity_type = EntityType::new_from_id(&self.id());
if entity_type.is_ok() {
number_of_fields += 1;
}
if !self.id().is_empty() {
number_of_fields += 1;
}
let mut state = serializer.serialize_struct("Entity", number_of_fields)?;
if !self.id().is_empty() {
state.serialize_field("id", &self.id())?;
}
match entity_type {
Ok(t) => {
state.serialize_field("type", &t.string_value())?;
}
_ => {}
}
let mut field = json!({});
self.labels()
.iter()
.for_each(|x| field[x.language.clone()] = json!(x));
state.serialize_field("labels", &field)?;
let mut field = json!({});
self.descriptions()
.iter()
.for_each(|x| field[x.language.clone()] = json!(x));
state.serialize_field("descriptions", &field)?;
let mut field = json!({});
self.aliases().iter().for_each(|x| {
let lang = x.language.clone();
match field[&lang].as_array_mut() {
None => field[&lang] = json!([]),
_ => {}
};
field[lang].as_array_mut().unwrap().push(json!(x));
});
state.serialize_field("aliases", &field)?;
let mut field = json!({});
match &self.sitelinks() {
Some(sitelinks) => {
sitelinks
.iter()
.for_each(|x| field[x.site.clone()] = json!(x));
state.serialize_field("sitelinks", &field)?;
}
None => state.serialize_field("sitelinks", &field)?,
}
let mut field = json!({});
self.claims().iter().for_each(|x| {
let prop = x.main_snak().property().clone();
match field[&prop].as_array_mut() {
None => field[&prop] = json!([]),
_ => {}
};
field[prop].as_array_mut().unwrap().push(json!(x));
});
state.serialize_field("claims", &field)?;
state.end()
}
}
impl TimeValue {
fn new_from_object(value: &serde_json::Map<String, serde_json::Value>) -> TimeValue {
Self {
after: value["after"].as_u64().unwrap_or_else(|| 0),
before: value["before"].as_u64().unwrap_or_else(|| 0),
calendarmodel: value["calendarmodel"]
.as_str()
.unwrap_or_else(|| "")
.to_string(),
precision: value["precision"].as_u64().unwrap_or_else(|| 0),
time: value["time"].as_str().unwrap_or_else(|| "").to_string(),
timezone: value["timezone"].as_u64().unwrap_or_else(|| 0),
}
}
pub fn new<S: Into<String>>(
after: u64,
before: u64,
calendarmodel: S,
precision: u64,
time: S,
timezone: u64,
) -> TimeValue {
Self {
after,
before,
calendarmodel: calendarmodel.into(),
precision,
time: time.into(),
timezone,
}
}
pub fn after(&self) -> &u64 {
&self.after
}
pub fn before(&self) -> &u64 {
&self.before
}
pub fn calendarmodel(&self) -> &str {
&self.calendarmodel
}
pub fn precision(&self) -> &u64 {
&self.precision
}
pub fn set_after(&mut self, after: u64) {
self.after = after;
}
pub fn set_before(&mut self, before: u64) {
self.before = before;
}
pub fn set_calendarmodel<S: Into<String>>(&mut self, calendarmodel: S) {
self.calendarmodel = calendarmodel.into();
}
pub fn set_precision(&mut self, precision: u64) {
self.precision = precision;
}
pub fn set_time<S: Into<String>>(&mut self, time: S) {
self.time = time.into();
}
pub fn set_timezone(&mut self, timezone: u64) {
self.timezone = timezone;
}
pub fn time(&self) -> &str {
&self.time
}
pub fn timezone(&self) -> &u64 {
&self.timezone
}
}
impl EntityValue {
pub fn new<S: Into<String>>(entity_type: EntityType, id: S) -> EntityValue {
Self {
entity_type,
id: id.into(),
}
}
pub fn new_from_object(
value: &serde_json::Map<String, serde_json::Value>,
) -> Result<EntityValue, WikibaseError> {
let entity_type_string = match value["entity-type"].as_str() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Entity type is not a string".to_string(),
));
}
};
let entity_type = match EntityType::new_from_str(entity_type_string) {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Entity type did not match".to_string(),
));
}
};
let id = match value["id"].as_str() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Id is not a string".to_string(),
));
}
};
Ok(Self {
entity_type,
id: id.to_string(),
})
}
pub fn entity_type(&self) -> &EntityType {
&self.entity_type
}
pub fn id(&self) -> &str {
&self.id
}
pub fn set_entity_type(&mut self, entity_type: EntityType) {
self.entity_type = entity_type;
}
pub fn set_id<S: Into<String>>(&mut self, id: S) {
self.id = id.into();
}
}
impl Serialize for EntityValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("EntityValue", 2)?;
state.serialize_field("entity-type", &self.entity_type)?;
state.serialize_field("id", &self.id)?;
state.end()
}
}
impl Reference {
pub fn new(snaks: Vec<Snak>) -> Reference {
Self { snaks }
}
pub fn set_snaks(&mut self, snaks: Vec<Snak>) {
self.snaks = snaks;
}
pub fn snaks(&self) -> &Vec<Snak> {
&self.snaks
}
}
impl DataValue {
pub fn new(value_type: DataValueType, value: Value) -> DataValue {
Self { value_type, value }
}
pub fn set_value(&mut self, value: Value) {
self.value = value;
}
pub fn set_value_type(&mut self, value_type: DataValueType) {
self.value_type = value_type;
}
pub fn value(&self) -> &Value {
&self.value
}
pub fn value_type(&self) -> &DataValueType {
&self.value_type
}
}
impl Serialize for DataValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("DataValue", 2)?;
state.serialize_field("type", &self.value_type)?;
state.serialize_field("value", &self.value)?;
state.end()
}
}
impl Snak {
pub fn new<S: Into<String>>(
datatype: SnakDataType,
property: S,
snak_type: SnakType,
data_value: Option<DataValue>,
) -> Snak {
Self {
datatype,
property: property.into(),
snak_type,
data_value,
}
}
pub fn new_time<S: Into<String>>(property: S, value: S, precision: u64) -> Snak {
Self::new(
SnakDataType::Time,
property,
SnakType::Value,
Some(DataValue::new(
DataValueType::Time,
Value::Time(TimeValue::new(
0,
0,
"http://www.wikidata.org/entity/Q1985727",
precision,
&value.into(),
0,
)),
)),
)
}
pub fn new_item<S: Into<String>>(property: S, entity_id: S) -> Snak {
Self::new(
SnakDataType::WikibaseItem,
property,
SnakType::Value,
Some(DataValue::new(
DataValueType::EntityId,
Value::Entity(EntityValue::new(EntityType::Item, entity_id.into())),
)),
)
}
pub fn new_string<S: Into<String>>(property: S, value: S) -> Snak {
Self::new(
SnakDataType::String,
property,
SnakType::Value,
Some(DataValue::new(
DataValueType::StringType,
Value::StringValue(value.into()),
)),
)
}
pub fn new_external_id<S: Into<String>>(property: S, value: S) -> Snak {
Self::new(
SnakDataType::ExternalId,
property,
SnakType::Value,
Some(DataValue::new(
DataValueType::StringType,
Value::StringValue(value.into()),
)),
)
}
pub fn new_coordinate<S: Into<String>>(property: S, latitude: f64, longitude: f64) -> Snak {
Self::new(
SnakDataType::GlobeCoordinate,
property,
SnakType::Value,
Some(DataValue::new(
DataValueType::GlobeCoordinate,
Value::Coordinate(Coordinate::new(
None,
"http://www.wikidata.org/entity/Q2".to_string(),
latitude,
longitude,
None,
)),
)),
)
}
pub fn new_monolingual_text<S: Into<String>>(property: S, language: S, value: S) -> Snak {
Self::new(
SnakDataType::MonolingualText,
property,
SnakType::Value,
Some(DataValue::new(
DataValueType::MonoLingualText,
Value::MonoLingual(MonoLingualText::new(language, value)),
)),
)
}
pub fn new_quantity<S: Into<String>>(property: S, amount: f64) -> Snak {
Self::new(
SnakDataType::Quantity,
property,
SnakType::Value,
Some(DataValue::new(
DataValueType::Quantity,
Value::Quantity(QuantityValue::new(amount, None, "1", None)),
)),
)
}
pub fn new_no_value<S: Into<String>>(property: S, datatype: SnakDataType) -> Snak {
Self::new(datatype, property.into(), SnakType::NoValue, None)
}
pub fn new_unknown_value<S: Into<String>>(property: S, datatype: SnakDataType) -> Snak {
Self::new(datatype, property.into(), SnakType::UnknownValue, None)
}
pub fn datatype(&self) -> &SnakDataType {
&self.datatype
}
pub fn data_value(&self) -> &Option<DataValue> {
&self.data_value
}
pub fn property(&self) -> &str {
&self.property
}
pub fn set_datatype(&mut self, datatype: SnakDataType) {
self.datatype = datatype;
}
pub fn set_data_value(&mut self, data_value: Option<DataValue>) {
self.data_value = data_value;
}
pub fn set_property(&mut self, property: &str) {
self.property = property.to_string();
}
pub fn set_snak_type(&mut self, snak_type: SnakType) {
self.snak_type = snak_type;
}
pub fn snak_type(&self) -> &SnakType {
&self.snak_type
}
}
impl Serialize for Snak {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut num = 3;
if self.data_value.is_some() {
num += 1;
}
let mut state = serializer.serialize_struct("Snak", num)?;
state.serialize_field("snaktype", &self.snak_type)?;
state.serialize_field("property", &self.property)?;
state.serialize_field("datatype", &self.datatype)?;
match &self.data_value {
Some(dv) => {
state.serialize_field("datavalue", &dv)?;
}
None => {}
}
state.end()
}
}
impl Statement {
pub fn new<S: Into<String>>(
claim_type: S,
rank: StatementRank,
main_snak: Snak,
qualifiers: Vec<Snak>,
references: Vec<Reference>,
) -> Statement {
Self {
claim_type: claim_type.into(),
rank,
main_snak,
qualifiers,
references,
id: None,
}
}
pub fn new_normal(
main_snak: Snak,
qualifiers: Vec<Snak>,
references: Vec<Reference>,
) -> Statement {
Self::new(
"statement",
StatementRank::Normal,
main_snak,
qualifiers,
references,
)
}
pub fn as_stripped_json(&self) -> serde_json::Value {
let mut ret = json!(self);
if ret.as_object().unwrap().contains_key("id") {
ret.as_object_mut().unwrap().remove("id").unwrap();
}
ret
}
pub fn set_id<S: Into<String>>(&mut self, id: S) {
self.id = Some(id.into());
}
pub fn id(&self) -> Option<String> {
self.id.clone()
}
pub fn set_claim_type(&mut self, claim_type: &str) {
self.claim_type = claim_type.to_string();
}
pub fn set_rank(&mut self, rank: StatementRank) {
self.rank = rank;
}
pub fn set_datatype(&mut self, datatype: SnakDataType) {
self.main_snak.datatype = datatype;
}
pub fn property(&self) -> &str {
&self.main_snak.property
}
pub fn set_property(&mut self, property: &str) {
self.main_snak.property = property.to_string();
}
pub fn set_main_snak(&mut self, snak: Snak) {
self.main_snak = snak;
}
pub fn add_qualifier_snak(&mut self, snak: Snak) {
self.qualifiers.push(snak);
}
pub fn set_qualifier_snaks(&mut self, snaks: Vec<Snak>) {
self.qualifiers = snaks;
}
pub fn set_references(&mut self, references: Vec<Reference>) {
self.references = references;
}
pub fn references(&self) -> &Vec<Reference> {
&self.references
}
pub fn qualifiers(&self) -> &Vec<Snak> {
&self.qualifiers
}
pub fn main_snak(&self) -> &Snak {
&self.main_snak
}
pub fn claim_type(&self) -> &String {
&self.claim_type
}
pub fn rank(&self) -> &StatementRank {
&self.rank
}
}
impl Serialize for Statement {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut num = 3;
if self.id.is_some() {
num += 1;
}
if !self.qualifiers.is_empty() {
num += 1;
}
if !self.references.is_empty() {
num += 1;
}
let mut state = serializer.serialize_struct("Statement", num)?;
state.serialize_field("type", &self.claim_type)?;
state.serialize_field("rank", &self.rank.as_str())?;
state.serialize_field("mainsnak", &self.main_snak)?;
match &self.id {
Some(id) => state.serialize_field("id", &id)?,
None => {}
};
if !self.qualifiers.is_empty() {
let mut qualifiers = json!({});
for snak in &self.qualifiers {
if !qualifiers[snak.property()].is_object() {
qualifiers[snak.property()] = json!([]);
}
qualifiers[snak.property()]
.as_array_mut()
.unwrap()
.push(json!(snak));
}
state.serialize_field("qualifiers", &qualifiers)?;
}
if !self.references.is_empty() {
let mut references = json!([]);
for reference in &self.references {
let mut rj = json!({"snaks":{}});
for snak in reference.snaks() {
if !rj["snaks"][snak.property()].is_array() {
rj["snaks"][snak.property()] = json!([]);
}
rj["snaks"][snak.property()]
.as_array_mut()
.unwrap()
.push(json!(snak));
}
references.as_array_mut().unwrap().push(rj);
}
state.serialize_field("references", &references)?;
}
state.end()
}
}