#![deny(
// missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize;
use std::str::FromStr;
use Coordinate;
use DataValue;
use DataValueType;
use EntityType;
use EntityValue;
use MonoLingualText;
use QuantityValue;
use TimeValue;
use Value;
use WikibaseError;
#[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",
}
}
pub fn from_str(s: &str) -> Result<Self, WikibaseError> {
match s {
"value" => Ok(SnakType::Value),
"novalue" => Ok(SnakType::NoValue),
"somevalue" => Ok(SnakType::UnknownValue),
_ => Err(WikibaseError::Serialization(format!(
"SnakType parsing error: {}",
s
))),
}
}
}
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, PartialEq, Copy)]
pub enum SnakDataType {
Time,
WikibaseItem,
WikibaseProperty,
WikibaseLexeme,
WikibaseSense,
WikibaseForm,
String,
ExternalId,
GlobeCoordinate,
MonolingualText,
Quantity,
Url,
CommonsMedia,
Math,
TabularData,
MusicalNotation,
GeoShape,
NotSet,
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),
"wikibase-property" => Ok(SnakDataType::WikibaseProperty),
"wikibase-lexeme" => Ok(SnakDataType::WikibaseLexeme),
"wikibase-sense" => Ok(SnakDataType::WikibaseSense),
"wikibase-form" => Ok(SnakDataType::WikibaseForm),
"external-id" => Ok(SnakDataType::ExternalId),
"string" => Ok(SnakDataType::String),
"globe-coordinate" => Ok(SnakDataType::GlobeCoordinate),
"monolingualtext" => Ok(SnakDataType::MonolingualText),
"quantity" => Ok(SnakDataType::Quantity),
"commonsMedia" => Ok(SnakDataType::CommonsMedia),
"url" => Ok(SnakDataType::Url),
"math" => Ok(SnakDataType::Math),
"tabular-data" => Ok(SnakDataType::TabularData),
"musical-notation" => Ok(SnakDataType::MusicalNotation),
"geo-shape" => Ok(SnakDataType::GeoShape),
"" => Ok(SnakDataType::NotSet),
"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::WikibaseProperty => serializer.serialize_str(&"wikibase-property"),
SnakDataType::WikibaseLexeme => serializer.serialize_str(&"wikibase-lexeme"),
SnakDataType::WikibaseSense => serializer.serialize_str(&"wikibase-sense"),
SnakDataType::WikibaseForm => serializer.serialize_str(&"wikibase-form"),
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::CommonsMedia => serializer.serialize_str(&"commonsMedia"),
SnakDataType::Url => serializer.serialize_str(&"url"),
SnakDataType::Math => serializer.serialize_str(&"math"),
SnakDataType::TabularData => serializer.serialize_str(&"tabular-data"),
SnakDataType::MusicalNotation => serializer.serialize_str(&"musical-notation"),
SnakDataType::GeoShape => serializer.serialize_str(&"geo-shape"),
SnakDataType::NotSet => serializer.serialize_str(&""), 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>,
}
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 new_from_json(
snak_json: &serde_json::Map<String, serde_json::Value>,
) -> Result<Snak, WikibaseError> {
let data_type_string = match snak_json.get("datatype") {
Some(value) => match value.as_str() {
Some(s) => s,
None => return Err(WikibaseError::Serialization("Datatype".to_string())),
}
None => "" };
let property = snak_json["property"].as_str().unwrap_or_else(|| "");
let snak_type_string = snak_json["snaktype"].as_str().unwrap_or_else(|| "");
let data_type = SnakDataType::from_str(data_type_string)?;
let snak_type = SnakType::from_str(snak_type_string)?;
if snak_type == SnakType::UnknownValue {
let snak = Snak::new(data_type, property, SnakType::UnknownValue, None);
return Ok(snak);
}
if snak_type == SnakType::NoValue {
let snak = Snak::new(data_type, property, SnakType::NoValue, None);
return Ok(snak);
}
let data_value = match snak_json["datavalue"].as_object() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Data value not found".to_string(),
));
}
};
let data_value_type = match data_value["type"].as_str() {
Some(value) => DataValueType::new_from_str(value)?,
None => {
return Err(WikibaseError::Serialization(
"Not type in data value".to_string(),
));
}
};
match data_value_type {
DataValueType::EntityId => {
let value_object = match data_value["value"].as_object() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Error serializing value object".to_string(),
));
}
};
let entity_value = EntityValue::new_from_object(value_object)?;
let statement_value = Value::Entity(entity_value);
let data_value = DataValue::new(data_value_type, statement_value);
Ok(Snak::new(
data_type,
property,
SnakType::Value,
Some(data_value),
))
}
DataValueType::Quantity => {
let value_object = match data_value["value"].as_object() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Error serializing value object".to_string(),
));
}
};
let quantity_value = QuantityValue::new_from_object(value_object)?;
let statement_value = Value::Quantity(quantity_value);
let data_value = DataValue::new(data_value_type, statement_value);
Ok(Snak::new(
data_type,
property,
SnakType::Value,
Some(data_value),
))
}
DataValueType::GlobeCoordinate => {
let value_object = match data_value["value"].as_object() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Error serializing value object".to_string(),
));
}
};
let coordinate = Coordinate::new_from_json(value_object)?;
let coordinate_statement = Value::Coordinate(coordinate);
let data_value = DataValue::new(data_value_type, coordinate_statement);
Ok(Snak::new(
data_type,
property,
SnakType::Value,
Some(data_value),
))
}
DataValueType::Time => {
let value_object = match data_value["value"].as_object() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Error serializing value object".to_string(),
));
}
};
let time_value_struct = TimeValue::new_from_object(value_object);
let time_value = Value::Time(time_value_struct);
let data_value = DataValue::new(data_value_type, time_value);
Ok(Snak::new(
data_type,
property,
SnakType::Value,
Some(data_value),
))
}
DataValueType::MonoLingualText => {
let value_object = match data_value["value"].as_object() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Error serializing value object".to_string(),
));
}
};
let mono_lingual_text = MonoLingualText::new_from_json(value_object)?;
let mono_lingual_text_value = Value::MonoLingual(mono_lingual_text);
let data_value = DataValue::new(data_value_type, mono_lingual_text_value);
Ok(Snak::new(
data_type,
property,
SnakType::Value,
Some(data_value),
))
}
DataValueType::StringType => {
let value_string = match data_value["value"].as_str() {
Some(value) => value,
None => {
return Err(WikibaseError::Serialization(
"Error serializing value string".to_string(),
));
}
};
let statement_value = Value::StringValue(value_string.to_string());
let data_value = DataValue::new(data_value_type, statement_value);
Ok(Snak::new(
data_type,
property,
SnakType::Value,
Some(data_value),
))
}
}
}
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()
}
}
#[cfg(test)]
mod tests {
use entity_container::EntityContainer;
use mediawiki::api::Api;
use std::collections::HashMap;
#[test]
fn test_datatypes() {
let api = Api::new("https://www.wikidata.org/w/api.php").unwrap();
let sparql = "SELECT (max(?property) as ?prop ) ?wbtype WHERE { ?property rdf:type wikibase:Property. ?property wikibase:propertyType ?wbtype} GROUP BY ?wbtype" ;
let mut prop2type: HashMap<String, String> = HashMap::new();
let sparql_result = api.sparql_query(sparql).unwrap();
for b in sparql_result["results"]["bindings"].as_array().unwrap() {
let prop = b["prop"]["value"].as_str().unwrap();
let prop = api.extract_entity_from_uri(prop).unwrap();
let wbtype = b["wbtype"]["value"].as_str().unwrap();
prop2type.insert(prop, wbtype.to_string());
}
let props: Vec<String> = prop2type
.iter()
.map(|(prop, _wbtype)| prop.to_string())
.collect();
let mut ec = EntityContainer::new();
ec.load_entities(&api, &props).unwrap();
for prop in &props {
match ec.get_entity(prop.to_owned()) {
Some(_i) => {}
None => panic!(format!(
"Could not get property {} for datatype {}",
&prop, &prop2type[prop]
)),
}
}
}
}