#![deny(
// missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
use configuration::*;
use deserialize::ToJson;
use from_json;
use item::ItemEntity;
use lexeme::{LexemeEntity, LexemeForm, LexemeSense};
use mediainfo::MediaInfoEntity;
use property::PropertyEntity;
use query::EntityQuery;
use serde::ser::Serializer;
use serde::Serialize;
use sitelink::*;
use statement::*;
use value::*;
use EntityType;
use LocaleString;
use SnakDataType;
use WikibaseError;
pub trait EntityTrait {
fn to_json(&self) -> serde_json::Value;
fn id_mut(&mut self) -> &mut String;
fn missing_mut(&mut self) -> &mut bool;
fn aliases_mut(&mut self) -> &mut Vec<LocaleString>;
fn labels_mut(&mut self) -> &mut Vec<LocaleString>;
fn descriptions_mut(&mut self) -> &mut Vec<LocaleString>;
fn claims_mut(&mut self) -> &mut Vec<Statement>;
fn sitelinks_mut(&mut self) -> &mut Option<Vec<SiteLink>>;
fn entity_type_mut(&mut self) -> &mut EntityType;
fn id(&self) -> &String;
fn missing(&self) -> bool;
fn aliases(&self) -> &Vec<LocaleString>;
fn labels(&self) -> &Vec<LocaleString>;
fn descriptions(&self) -> &Vec<LocaleString>;
fn claims(&self) -> &Vec<Statement>;
fn sitelinks(&self) -> &Option<Vec<SiteLink>>;
fn entity_type(&self) -> &EntityType;
fn label_in_locale(&self, locale: &str) -> Option<&str> {
for label in self.labels().iter() {
if label.language() == locale {
return Some(label.value());
}
}
None
}
fn description_in_locale(&self, locale: &str) -> Option<&str> {
for description in self.descriptions().iter() {
if description.language() == locale {
return Some(description.value());
}
}
None
}
fn set_aliases(&mut self, aliases: Vec<LocaleString>) {
*self.aliases_mut() = aliases;
}
fn set_claims(&mut self, claims: Vec<Statement>) {
*self.claims_mut() = claims;
}
fn add_claim(&mut self, claim: Statement) {
self.claims_mut().push(claim);
}
fn set_descriptions(&mut self, descriptions: Vec<LocaleString>) {
*self.descriptions_mut() = descriptions;
}
fn set_id(&mut self, id: String) {
*self.id_mut() = id;
}
fn set_labels(&mut self, labels: Vec<LocaleString>) {
*self.labels_mut() = labels;
}
fn set_missing(&mut self, missing: bool) {
*self.missing_mut() = missing;
}
fn set_sitelinks(&mut self, sitelinks: Option<Vec<SiteLink>>) {
*self.sitelinks_mut() = sitelinks;
}
fn set_sitelink(&mut self, new_sitelink: SiteLink) {
let current_sitelinks = self.sitelinks_mut();
match current_sitelinks {
None => {
*current_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),
MediaInfo(MediaInfoEntity),
Lexeme(LexemeEntity),
}
impl ToJson for Entity {}
impl EntityTrait for Entity {
fn id(&self) -> &String {
self.inner().id()
}
fn missing(&self) -> bool {
self.inner().missing()
}
fn aliases(&self) -> &Vec<LocaleString> {
self.inner().aliases()
}
fn labels(&self) -> &Vec<LocaleString> {
self.inner().labels()
}
fn descriptions(&self) -> &Vec<LocaleString> {
self.inner().descriptions()
}
fn claims(&self) -> &Vec<Statement> {
self.inner().claims()
}
fn sitelinks(&self) -> &Option<Vec<SiteLink>> {
self.inner().sitelinks()
}
fn id_mut(&mut self) -> &mut String {
self.inner_mut().id_mut()
}
fn missing_mut(&mut self) -> &mut bool {
self.inner_mut().missing_mut()
}
fn aliases_mut(&mut self) -> &mut Vec<LocaleString> {
self.inner_mut().aliases_mut()
}
fn labels_mut(&mut self) -> &mut Vec<LocaleString> {
self.inner_mut().labels_mut()
}
fn descriptions_mut(&mut self) -> &mut Vec<LocaleString> {
self.inner_mut().descriptions_mut()
}
fn sitelinks_mut(&mut self) -> &mut Option<Vec<SiteLink>> {
self.inner_mut().sitelinks_mut()
}
fn claims_mut(&mut self) -> &mut Vec<Statement> {
self.inner_mut().claims_mut()
}
fn entity_type(&self) -> &EntityType {
self.inner().entity_type()
}
fn entity_type_mut(&mut self) -> &mut EntityType {
self.inner_mut().entity_type_mut()
}
fn to_json(&self) -> serde_json::Value {
self.inner().to_json()
}
}
impl Entity {
pub fn inner(&self) -> &EntityTrait {
match self {
Entity::Item(e) => e,
Entity::Property(e) => e,
Entity::MediaInfo(e) => e,
Entity::Lexeme(e) => e,
}
}
pub fn inner_mut(&mut self) -> &mut EntityTrait {
match self {
Entity::Item(e) => e,
Entity::Property(e) => e,
Entity::MediaInfo(e) => e,
Entity::Lexeme(e) => e,
}
}
pub fn new_from_json(json: &serde_json::Value) -> Result<Entity, WikibaseError> {
let id = match &json["id"].as_str() {
&Some(value) => value.to_string(),
&None => return Err(WikibaseError::Serialization("ID missing".to_string())),
};
let entity_type = match EntityType::new_from_id(&id) {
Ok(et) => et,
Err(_) => {
return Err(WikibaseError::Serialization(
"Type can not be inferred from ID".to_string(),
));
}
};
match entity_type {
EntityType::Item => Ok(Entity::Item(ItemEntity::new_from_json(json)?)),
EntityType::Property => Ok(Entity::Property(PropertyEntity::new_from_json(json)?)),
EntityType::MediaInfo => Ok(Entity::MediaInfo(MediaInfoEntity::new_from_json(json)?)),
EntityType::Lexeme => Ok(Entity::Lexeme(LexemeEntity::new_from_json(json)?)),
EntityType::LexemeSense => Err(WikibaseError::Serialization(format!(
"Trying to load a LexemeSense: {}",
&json
))),
EntityType::LexemeForm => Err(WikibaseError::Serialization(format!(
"Trying to load a LexemeForm: {}",
&json
))),
}
}
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_empty_mediainfo() -> Self {
Entity::MediaInfo(MediaInfoEntity::new_empty())
}
pub fn new_empty_lexeme() -> Self {
Entity::Lexeme(LexemeEntity::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>,
datatype: Option<SnakDataType>,
missing: bool,
) -> Self {
let mut e =
PropertyEntity::new(id, labels, descriptions, aliases, claims, datatype, missing);
e.set_datatype(datatype);
Entity::Property(e)
}
pub fn new_mediainfo(
id: String,
labels: Vec<LocaleString>,
descriptions: Vec<LocaleString>,
claims: Vec<Statement>,
missing: bool,
) -> Self {
Entity::MediaInfo(MediaInfoEntity::new(
id,
labels,
descriptions,
claims,
missing,
))
}
pub fn new_lexeme(
id: String,
labels: Vec<LocaleString>,
claims: Vec<Statement>,
language: String,
lexical_category: Option<String>,
forms: Vec<LexemeForm>,
senses: Vec<LexemeSense>,
missing: bool,
) -> Self {
Entity::Lexeme(LexemeEntity::new(
id,
labels,
claims,
language,
lexical_category,
forms,
senses,
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 set_label(&mut self, new_label: LocaleString) {
let labels = self.labels_mut();
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 language: String = language.into();
self.labels_mut().retain(|x| *x.language() != language);
}
pub fn remove_description<S: Into<String>>(&mut self, language: S) {
let language: String = language.into();
self.descriptions_mut()
.retain(|x| *x.language() != language);
}
pub fn set_sitelink(&mut self, new_sitelink: SiteLink) {
self.inner_mut().set_sitelink(new_sitelink);
}
pub fn remove_sitelink<S: Into<String>>(&mut self, site: S) {
let sitelinks = self.sitelinks_mut();
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 = self.descriptions_mut();
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 = self.aliases_mut();
if !aliases.contains(&new_alias) {
aliases.push(new_alias);
}
}
pub fn remove_alias(&mut self, alias: LocaleString) {
let aliases = self.aliases_mut();
aliases.retain(|x| *x != alias);
}
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,
}
}
}
impl Serialize for Entity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self.missing() {
true => serializer.serialize_none(),
false => serializer.serialize_some(&self.to_json()),
}
}
}
#[cfg(test)]
mod tests {
use entity_container::*;
use mediawiki::api::Api;
#[test]
fn entity_serialization() {
let api = Api::new("https://www.wikidata.org/w/api.php").unwrap();
let mut ec = EntityContainer::new();
let the_count = ec.load_entity(&api, "Q12345").unwrap();
let j = json!(&the_count);
assert_eq!(j["id"].as_str().unwrap(), "Q12345");
assert_eq!(j["type"].as_str().unwrap(), "item");
assert_eq!(
j["labels"]["en"]["value"].as_str().unwrap(),
"Count von Count"
);
assert_eq!(
j["descriptions"]["en"]["value"].as_str().unwrap(),
"character on Sesame Street"
);
assert_eq!(
j["aliases"]["en"][0]["value"].as_str().unwrap(),
"The Count"
);
assert_eq!(
j["sitelinks"]["enwiki"]["title"].as_str().unwrap(),
"Count von Count"
);
assert_eq!(
j["claims"]["P3478"][0]["mainsnak"]["datavalue"]["value"]
.as_str()
.unwrap(),
"850116"
);
}
}