extern crate mediawiki;
use crate::*;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub enum EditTarget {
Entity(String),
New(EntityType),
}
#[derive(Debug)]
pub enum EntityDiffParamState {
All,
None,
Some(Vec<String>),
Except(Vec<String>),
}
impl EntityDiffParamState {
pub fn some(list: &Vec<&str>) -> EntityDiffParamState {
EntityDiffParamState::Some(list.iter().map(|s| s.to_string()).collect())
}
pub fn except(list: &Vec<&str>) -> EntityDiffParamState {
EntityDiffParamState::Except(list.iter().map(|s| s.to_string()).collect())
}
pub fn valid<S: Into<String>>(&self, key: S) -> bool {
match self {
EntityDiffParamState::All => true,
EntityDiffParamState::None => false,
EntityDiffParamState::Some(v) => v.contains(&key.into()),
EntityDiffParamState::Except(v) => !v.contains(&key.into()),
}
}
}
#[derive(Debug)]
pub struct EntityDiffParam {
pub add: EntityDiffParamState,
pub remove: EntityDiffParamState,
pub alter: EntityDiffParamState,
}
impl EntityDiffParam {
pub fn all() -> EntityDiffParam {
EntityDiffParam {
add: EntityDiffParamState::All,
remove: EntityDiffParamState::All,
alter: EntityDiffParamState::All,
}
}
pub fn none() -> EntityDiffParam {
EntityDiffParam {
add: EntityDiffParamState::None,
remove: EntityDiffParamState::None,
alter: EntityDiffParamState::None,
}
}
pub fn some(list: &Vec<&str>) -> EntityDiffParam {
EntityDiffParam {
add: EntityDiffParamState::some(list),
remove: EntityDiffParamState::some(list),
alter: EntityDiffParamState::some(list),
}
}
pub fn except(list: &Vec<&str>) -> EntityDiffParam {
EntityDiffParam {
add: EntityDiffParamState::except(list),
remove: EntityDiffParamState::except(list),
alter: EntityDiffParamState::except(list),
}
}
pub fn valid<S: Into<String>>(&self, key: S, action: &str) -> bool {
match action {
"add" => self.add.valid(key),
"remove" => self.remove.valid(key),
"alter" => self.alter.valid(key),
_ => panic!("Bad mode '{}' in EntityDiffParam::valid", &action),
}
}
}
#[derive(Debug)]
pub struct EntityDiffParamSub {
pub list: Vec<(EntityDiffParamState, EntityDiffParamState)>,
}
impl EntityDiffParamSub {
pub fn all() -> EntityDiffParamSub {
EntityDiffParamSub {
list: vec![(EntityDiffParamState::All, EntityDiffParamState::All)],
}
}
pub fn none() -> EntityDiffParamSub {
EntityDiffParamSub { list: vec![] }
}
pub fn valid<S: Into<String>>(&self, prop1: S, prop2: S) -> bool {
let prop1: String = prop1.into();
let prop2: String = prop2.into();
for entry in &self.list {
if entry.0.valid(prop1.clone()) && entry.1.valid(prop2.clone()) {
return true;
}
}
false
}
}
#[derive(Debug)]
pub struct EntityDiffParams {
pub labels: EntityDiffParam,
pub descriptions: EntityDiffParam,
pub aliases: EntityDiffParam,
pub claims: EntityDiffParam,
pub sitelinks: EntityDiffParam,
pub references: EntityDiffParamSub,
pub qualifiers: EntityDiffParamSub,
}
impl EntityDiffParams {
pub fn all() -> EntityDiffParams {
EntityDiffParams {
labels: EntityDiffParam::all(),
descriptions: EntityDiffParam::all(),
aliases: EntityDiffParam::all(),
claims: EntityDiffParam::all(),
sitelinks: EntityDiffParam::all(),
references: EntityDiffParamSub::all(),
qualifiers: EntityDiffParamSub::all(),
}
}
pub fn none() -> EntityDiffParams {
EntityDiffParams {
labels: EntityDiffParam::none(),
descriptions: EntityDiffParam::none(),
aliases: EntityDiffParam::none(),
claims: EntityDiffParam::none(),
sitelinks: EntityDiffParam::none(),
references: EntityDiffParamSub::none(),
qualifiers: EntityDiffParamSub::none(),
}
}
}
#[derive(Debug, PartialEq)]
enum EntityDiffClaimComparison {
Same,
Similar,
Different,
}
#[derive(Debug)]
pub struct EntityDiff {
j: serde_json::Value,
edit_target: EditTarget,
edit_summary: Option<String>,
log: Vec<String>,
}
impl EntityDiff {
pub fn new(i1: &Entity, i2: &Entity, params: &EntityDiffParams) -> EntityDiff {
let edit_target = if i1.id().is_empty() {
EditTarget::New(*i1.entity_type())
} else {
EditTarget::Entity(i1.id().to_string())
};
let mut ret = EntityDiff {
j: json!({}),
edit_summary: None,
log: vec![],
edit_target,
};
ret.diff(i1, i2, params);
ret
}
pub fn new_as_result(
i1: &Entity,
i2: &Entity,
params: &EntityDiffParams,
) -> Result<EntityDiff, Vec<String>> {
let ret = EntityDiff::new(i1, i2, params);
match ret.log.is_empty() {
true => Ok(ret),
false => Err(ret.log),
}
}
pub fn actions(&self) -> &serde_json::Value {
&self.j
}
pub fn actions_mut(&mut self) -> &mut serde_json::Value {
&mut self.j
}
pub fn as_str(&self) -> Result<String, serde_json::Error> {
::serde_json::to_string(&self.j)
}
pub fn to_string_pretty(&self) -> Result<String, serde_json::Error> {
::serde_json::to_string_pretty(&self.j)
}
pub fn edit_summary(&self) -> &Option<String> {
&self.edit_summary
}
pub fn set_edit_summary(&mut self, edit_summary: Option<String>) {
self.edit_summary = edit_summary;
}
pub fn is_empty(&self) -> bool {
match self.j.as_object() {
Some(o) => o.is_empty(),
None => true,
}
}
fn diff(&mut self, i1: &Entity, i2: &Entity, params: &EntityDiffParams) {
self.diff_labels(i1, i2, params);
self.diff_descriptions(i1, i2, params);
self.diff_aliases(i1, i2, params);
self.diff_claims(i1, i2, params);
self.diff_sitelinks(i1, i2, params);
}
fn compare_snak_values(&self, value1: &Value, value2: &Value) -> EntityDiffClaimComparison {
match (value1, value2) {
(Value::Coordinate(v1), Value::Coordinate(v2)) => {
if v1.globe() == v2.globe()
&& v1.latitude() == v2.latitude()
&& v1.longitude() == v2.longitude()
{
return EntityDiffClaimComparison::Same;
}
}
(Value::MonoLingual(v1), Value::MonoLingual(v2)) => {
if v1 == v2 {
return EntityDiffClaimComparison::Same;
}
}
(Value::Entity(v1), Value::Entity(v2)) => {
if v1 == v2 {
return EntityDiffClaimComparison::Same;
}
}
(Value::EntitySchema(v1), Value::EntitySchema(v2)) => {
if v1 == v2 {
return EntityDiffClaimComparison::Same;
}
}
(Value::Quantity(v1), Value::Quantity(v2)) => {
if v1.amount() == v2.amount() && v1.unit() == v2.unit() {
return EntityDiffClaimComparison::Same;
}
}
(Value::StringValue(v1), Value::StringValue(v2)) => {
if v1 == v2 {
return EntityDiffClaimComparison::Same;
}
}
(Value::Time(v1), Value::Time(v2)) => {
if v1.calendarmodel() == v2.calendarmodel()
&& v1.precision() == v2.precision()
&& v1.time() == v2.time()
{
return EntityDiffClaimComparison::Same;
}
}
_ => {}
}
EntityDiffClaimComparison::Different
}
fn compare_snaks(&self, s1: &Snak, s2: &Snak) -> EntityDiffClaimComparison {
if s1.property() != s2.property() {
return EntityDiffClaimComparison::Different;
}
if s1.snak_type() != s2.snak_type() {
return EntityDiffClaimComparison::Different;
}
match (s1.data_value(), s2.data_value()) {
(None, None) => EntityDiffClaimComparison::Same,
(None, Some(_)) => EntityDiffClaimComparison::Different,
(Some(_), None) => EntityDiffClaimComparison::Different,
(Some(dv1), Some(dv2)) => {
if dv1.value_type() != dv2.value_type() {
return EntityDiffClaimComparison::Different;
}
self.compare_snak_values(dv1.value(), dv2.value())
}
}
}
fn compare_snaks_sub(
&self,
base_snak: &Snak,
snak1: &Snak,
snak2: &Snak,
params_sub: &EntityDiffParamSub,
) -> EntityDiffClaimComparison {
match params_sub.valid(base_snak.property(), snak1.property()) {
true => self.compare_snaks(snak1, snak2),
false => EntityDiffClaimComparison::Same,
}
}
fn compare_ref_or_qual_sub(
&self,
base_snak: &Snak,
list1: &[Snak],
list2: &[Snak],
params_sub: &EntityDiffParamSub,
) -> EntityDiffClaimComparison {
for x1 in list1 {
if !params_sub.valid(base_snak.property(), x1.property()) {
continue;
}
if !list2
.iter()
.map(|x2| self.compare_snaks_sub(base_snak, x1, x2, params_sub))
.any(|x2| x2 == EntityDiffClaimComparison::Same)
{
return EntityDiffClaimComparison::Similar;
}
}
EntityDiffClaimComparison::Same
}
fn compare_ref_or_qual(
&self,
base_snak: &Snak,
list1: &[Snak],
list2: &[Snak],
params_sub: &EntityDiffParamSub,
) -> EntityDiffClaimComparison {
match self.compare_ref_or_qual_sub(base_snak, list1, list2, params_sub) {
EntityDiffClaimComparison::Same => {
self.compare_ref_or_qual_sub(base_snak, list2, list1, params_sub)
}
other => other,
}
}
fn compare_qualifiers(
&self,
base_snak: &Snak,
qualifiers1: &[Snak],
qualifiers2: &[Snak],
params: &EntityDiffParams,
) -> EntityDiffClaimComparison {
self.compare_ref_or_qual(base_snak, qualifiers1, qualifiers2, ¶ms.qualifiers)
}
fn compare_reference_group(
&self,
base_snak: &Snak,
reference1: &Reference,
reference2: &Reference,
params: &EntityDiffParams,
) -> EntityDiffClaimComparison {
self.compare_ref_or_qual(
base_snak,
reference1.snaks(),
reference2.snaks(),
¶ms.references,
)
}
fn compare_references(
&self,
base_snak: &Snak,
references1: &[Reference],
references2: &[Reference],
params: &EntityDiffParams,
) -> EntityDiffClaimComparison {
if references1.len() != references2.len() {
return EntityDiffClaimComparison::Similar;
}
for rg1 in references1 {
let mut found = false;
for rg2 in references2 {
if let EntityDiffClaimComparison::Same =
self.compare_reference_group(base_snak, rg1, rg2, params)
{
found = true
}
}
if !found {
return EntityDiffClaimComparison::Similar;
}
}
for rg2 in references2 {
let mut found = false;
for rg1 in references1 {
if let EntityDiffClaimComparison::Same =
self.compare_reference_group(base_snak, rg1, rg2, params)
{
found = true
}
}
if !found {
return EntityDiffClaimComparison::Similar;
}
}
EntityDiffClaimComparison::Same
}
fn compare_claims(
&self,
s1: &Statement,
s2: &Statement,
params: &EntityDiffParams,
) -> EntityDiffClaimComparison {
if s1.claim_type() != s2.claim_type() {
return EntityDiffClaimComparison::Different;
}
match self.compare_snaks(s1.main_snak(), s2.main_snak()) {
EntityDiffClaimComparison::Same => {}
ret => return ret,
}
if s1.rank() != s2.rank() {
return EntityDiffClaimComparison::Similar;
}
match self.compare_qualifiers(s1.main_snak(), s1.qualifiers(), s2.qualifiers(), params) {
EntityDiffClaimComparison::Same => {}
ret => return ret,
}
match self.compare_references(s1.main_snak(), s1.references(), s2.references(), params) {
EntityDiffClaimComparison::Same => {}
ret => return ret,
}
EntityDiffClaimComparison::Same
}
fn get_claim_property(&self, s: &Statement) -> String {
s.main_snak().property().to_string()
}
fn log_issue(&mut self, issue: String) {
self.log.push(issue);
}
fn diff_claims(&mut self, i1: &Entity, i2: &Entity, params: &EntityDiffParams) {
for c1 in i1.claims() {
let mut found = false;
for c2 in i2.claims() {
match self.compare_claims(c1, c2, params) {
EntityDiffClaimComparison::Same => found = true,
EntityDiffClaimComparison::Similar => {
if params.claims.valid(self.get_claim_property(c1), "alter") {
match c1.id() {
Some(id) => {
self.ensure_diff_j("claims");
let mut v = c2.as_stripped_json();
v["id"] = serde_json::Value::String(id);
self.add_to_claims(v);
}
None => self.log_issue(format!(
"Trying to alter a statement with no ID:{:?}",
&c1
)),
}
}
found = true;
break;
}
EntityDiffClaimComparison::Different => {}
}
}
if !found && params.claims.valid(self.get_claim_property(c1), "remove") {
match c1.id() {
Some(id) => {
self.ensure_diff_j("claims");
let v = json!({"id":id,"remove":""});
self.add_to_claims(v);
}
None => {
self.log_issue(format!("Trying to remove a statement with no ID:{c1:?}"))
}
}
}
}
for c2 in i2.claims() {
let mut found = false;
for c1 in i1.claims() {
match self.compare_claims(c1, c2, params) {
EntityDiffClaimComparison::Same => found = true,
EntityDiffClaimComparison::Similar => found = true, _ => {}
}
}
if !found && params.claims.valid(self.get_claim_property(c2), "add") {
self.ensure_diff_j("claims");
let v = c2.as_stripped_json();
self.add_to_claims(v);
}
}
}
fn add_to_json_array(&mut self, v: serde_json::Value, key: &str) {
if let Some(a) = self.j[key].as_array_mut() {
a.push(v)
}
}
fn add_to_claims(&mut self, v: serde_json::Value) {
self.add_to_json_array(v, "claims")
}
fn ensure_diff_j(&mut self, key: &str) {
if !self.j[key].is_array() {
self.j[key] = json!([]);
}
}
fn diff_sitelinks(&mut self, i1: &Entity, i2: &Entity, params: &EntityDiffParams) {
match (i1.sitelinks(), i2.sitelinks()) {
(Some(sl1), Some(sl2)) => {
for s1 in sl1 {
let mut found = false;
for s2 in sl2 {
if s1 == s2 {
found = true;
} else if s1.site() == s2.site()
&& params.sitelinks.valid(s2.site().as_str(), "alter")
{
match serde_json::to_value(s2) {
Ok(v) => self.j["sitelinks"][s1.site()] = v,
Err(e) => {
self.log_issue(format!("diff_sitelinks(1): {:?}", e));
continue;
}
}
found = true;
}
}
if !found && params.sitelinks.valid(s1.site().as_str(), "remove") {
match serde_json::to_value(s1) {
Ok(v) => self.j["sitelinks"][s1.site()] = v,
Err(e) => {
self.log_issue(format!("diff_sitelinks(2): {:?}", e));
continue;
}
}
self.j["sitelinks"][s1.site()]["remove"] = json!("");
}
}
for s2 in sl2 {
let mut found = false;
for s1 in sl1 {
if s1 == s2 {
found = true;
}
}
if !found && params.sitelinks.valid(s2.site().as_str(), "add") {
match serde_json::to_value(s2) {
Ok(v) => self.j["sitelinks"][s2.site()] = v,
Err(e) => {
self.log_issue(format!("diff_sitelinks(3): {:?}", e));
continue;
}
}
}
}
}
(Some(sl1), None) => {
for sl in sl1 {
if params.sitelinks.valid(sl.site().as_str(), "remove") {
self.j["sitelinks"][sl.site()] =
json!({"site":sl.site(),"title":sl.title(),"remove":""});
}
}
}
(None, Some(sl2)) => {
for sl in sl2 {
if params.sitelinks.valid(sl.site().as_str(), "add") {
match serde_json::to_value(sl) {
Ok(v) => self.j["sitelinks"][sl.site()] = v,
Err(e) => {
self.log_issue(format!("diff_sitelinks(4): {:?}", e));
continue;
}
}
}
}
}
(None, None) => {}
}
}
fn diff_labels(&mut self, i1: &Entity, i2: &Entity, params: &EntityDiffParams) {
self.diff_locales(i1.labels(), i2.labels(), ¶ms.labels, "labels");
}
fn diff_descriptions(&mut self, i1: &Entity, i2: &Entity, params: &EntityDiffParams) {
self.diff_locales(
i1.descriptions(),
i2.descriptions(),
¶ms.descriptions,
"descriptions",
);
}
fn diff_aliases(&mut self, i1: &Entity, i2: &Entity, params: &EntityDiffParams) {
self.diff_locales(i1.aliases(), i2.aliases(), ¶ms.aliases, "aliases");
}
fn diff_locales(
&mut self,
l1: &[LocaleString],
l2: &[LocaleString],
params: &EntityDiffParam,
mode: &str,
) {
for s1 in l1 {
let mut found = false;
for s2 in l2 {
if s1.language() == s2.language() {
if s1.value() == s2.value() {
found = true;
} else if mode != "aliases" && params.valid(s1.language(), "alter") {
self.j[mode][s2.language()] =
json!({"language":s2.language(),"value":s2.value()});
found = true;
}
}
}
if !found && params.valid(s1.language(), "remove") {
let v = json!({"language":s1.language(),"value":s1.value(),"remove":""});
self.ensure_diff_j(mode);
self.add_to_json_array(v, mode);
}
}
for s2 in l2 {
let mut found = false;
for s1 in l1 {
if s1 == s2 {
found = true;
}
}
if !found && params.valid(s2.language(), "add") {
let mut v = json!({"language":s2.language(),"value":s2.value()});
if mode == "aliases" {
v["add"] = json!("");
}
self.ensure_diff_j(mode);
self.add_to_json_array(v, mode);
}
}
}
pub fn edit_target(&self) -> &EditTarget {
&self.edit_target
}
pub fn set_edit_target(&mut self, edit_target: EditTarget) {
self.edit_target = edit_target;
}
pub async fn apply_diff(
&self,
mw_api: &mut mediawiki::api::Api,
diff: &EntityDiff,
) -> Result<serde_json::Value, WikibaseError> {
let json = diff.as_str().map_err(|e| {
WikibaseError::Diff(format!("Failed to apply diff '{:?}': '{:?}'", &diff, e))
})?;
let token = mw_api.get_edit_token().await?;
let mut params: HashMap<String, String> = vec![
("action".to_string(), "wbeditentity".to_string()),
("data".to_string(), json.to_string()),
("token".to_string(), token.to_string()),
]
.into_iter()
.collect();
if let Some(s) = &self.edit_summary {
params.insert("summary".to_string(), s.to_string());
}
match &self.edit_target {
EditTarget::Entity(id) => {
params.insert("id".to_string(), id.to_string());
}
EditTarget::New(entity_type) => {
params.insert("new".to_string(), entity_type.string_value());
}
};
let res = mw_api.post_query_api_json(¶ms).await?;
if let Some(num) = res["success"].as_i64()
&& num == 1
{
match &res["entity"] {
serde_json::Value::Null => {}
entity_json => {
return Ok(entity_json.to_owned());
}
}
}
Err(WikibaseError::Diff(format!(
"Failed to apply diff '{:?}', result:{:?}",
&diff, &res
)))
}
pub fn get_entity_id(entity_json: &serde_json::Value) -> Option<String> {
match &entity_json["id"] {
serde_json::Value::String(s) => Some(s.to_string()),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::{EntityDiff, EntityDiffParamState, EntityDiffParams}; use crate::*;
fn dummy_item_ref_qual(qualifiers: &[Snak], references: &[Reference]) -> Entity {
let mut ret = Entity::new_empty_item();
ret.add_claim(Statement::new_normal(
Snak::new_item("P31", "Q12345"),
qualifiers.to_vec(),
references.to_vec(),
));
ret.claims_mut()[0].set_id("dummy1");
ret
}
fn ref1() -> Reference {
Reference::new(qual1())
}
fn ref2() -> Reference {
Reference::new(qual2())
}
fn ref3() -> Reference {
Reference::new(qual3())
}
fn qual1() -> Vec<Snak> {
vec![
Snak::new_item("P248", "Q5531047"),
Snak::new_time("P813", "+2019-04-12T01:23:45Z", 11),
]
}
fn qual2() -> Vec<Snak> {
vec![
Snak::new_item("P248", "Q5531047"),
Snak::new_time("P813", "+2018-04-12T01:23:45Z", 11),
]
}
fn qual3() -> Vec<Snak> {
vec![Snak::new_item("P248", "Q5531047")]
}
fn claim1() -> Statement {
Statement::new_normal(Snak::new_item("P31", "Q12345"), vec![], vec![])
}
fn claim2() -> Statement {
Statement::new_normal(Snak::new_string("P225", "unicorn"), vec![], vec![])
}
#[test]
fn qualifiers_diff_blank2one() {
let i1 = dummy_item_ref_qual(&[], &[]);
let i2 = dummy_item_ref_qual(&qual1(), &[]);
let params = EntityDiffParams::all();
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn qualifiers_diff_same() {
let i1 = dummy_item_ref_qual(&qual1(), &[]);
let i2 = dummy_item_ref_qual(&qual1(), &[]);
let params = EntityDiffParams::all();
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn qualifiers_diff_different() {
let i1 = dummy_item_ref_qual(&qual1(), &[]);
let i2 = dummy_item_ref_qual(&qual2(), &[]);
let params = EntityDiffParams::all();
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn qualifiers_diff_different_order() {
let mut q1r = qual1();
let i1 = dummy_item_ref_qual(&q1r, &[]);
q1r.reverse();
let i2 = dummy_item_ref_qual(&q1r, &[]);
let params = EntityDiffParams::all();
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn qualifiers_diff_different_tolerant() {
let i1 = dummy_item_ref_qual(&qual1(), &[]);
let i2 = dummy_item_ref_qual(&qual2(), &[]);
let mut params = EntityDiffParams::all();
params.qualifiers.list = vec![(
EntityDiffParamState::All,
EntityDiffParamState::except(&vec!["P813"]),
)];
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
params.qualifiers.list = vec![(
EntityDiffParamState::All,
EntityDiffParamState::some(&vec!["P813"]),
)];
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn references_diff_blank2one() {
let i1 = dummy_item_ref_qual(&[], &[]);
let i2 = dummy_item_ref_qual(&[], &[ref1()]);
let params = EntityDiffParams::all();
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn references_diff_same() {
let i1 = dummy_item_ref_qual(&[], &[ref1()]);
let i2 = dummy_item_ref_qual(&[], &[ref1()]);
let params = EntityDiffParams::all();
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn references_diff_different() {
let i1 = dummy_item_ref_qual(&[], &[ref1()]);
let i2 = dummy_item_ref_qual(&[], &[ref2()]);
let params = EntityDiffParams::all();
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn references_diff_different_order() {
let i1 = dummy_item_ref_qual(&[], &[ref1(), ref2()]);
let i2 = dummy_item_ref_qual(&[], &[ref2(), ref1()]);
let params = EntityDiffParams::all();
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn references_diff_different_tolerant() {
let i1 = dummy_item_ref_qual(&[], &[ref1()]);
let i2 = dummy_item_ref_qual(&[], &[ref2()]);
let mut params = EntityDiffParams::all();
params.references.list = vec![(
EntityDiffParamState::All,
EntityDiffParamState::except(&vec!["P813"]),
)];
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
params.references.list = vec![(
EntityDiffParamState::All,
EntityDiffParamState::some(&vec!["P813"]),
)];
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn references_diff_different_tolerant_missing() {
let i1 = dummy_item_ref_qual(&[], &[ref1()]);
let i2 = dummy_item_ref_qual(&[], &[ref3()]);
let mut params = EntityDiffParams::all();
params.references.list = vec![(
EntityDiffParamState::All,
EntityDiffParamState::except(&vec!["P813"]),
)];
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
params.references.list = vec![(
EntityDiffParamState::All,
EntityDiffParamState::some(&vec!["P813"]),
)];
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn references_diff_different_tolerant_wrong_prop() {
let i1 = dummy_item_ref_qual(&[], &[ref1()]);
let i2 = dummy_item_ref_qual(&[], &[ref2()]);
let mut params = EntityDiffParams::all();
params.references.list = vec![(
EntityDiffParamState::All,
EntityDiffParamState::except(&vec!["P123456789"]),
)];
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn labels_misc() {
let mut i1 = Entity::new_empty_item();
let mut i2 = Entity::new_empty_item();
let params = EntityDiffParams::all();
i1.set_label(LocaleString::new("en", "blah1"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.set_label(LocaleString::new("en", "blah1"));
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.set_label(LocaleString::new("en", "blah2"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.remove_label("en");
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.remove_label("en");
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.set_label(LocaleString::new("en", "blah1"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn descriptions_misc() {
let mut i1 = Entity::new_empty_item();
let mut i2 = Entity::new_empty_item();
let params = EntityDiffParams::all();
i1.set_description(LocaleString::new("en", "blah1"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.set_description(LocaleString::new("en", "blah1"));
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.set_description(LocaleString::new("en", "blah2"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.remove_description("en");
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.remove_description("en");
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.set_description(LocaleString::new("en", "blah1"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn aliases_misc() {
let mut i1 = Entity::new_empty_item();
let mut i2 = Entity::new_empty_item();
let params = EntityDiffParams::all();
i1.add_alias(LocaleString::new("en", "blah1"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.add_alias(LocaleString::new("en", "blah1"));
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.add_alias(LocaleString::new("en", "blah2"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.add_alias(LocaleString::new("en", "blah2"));
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.remove_alias(LocaleString::new("en", "blah1"));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.remove_alias(LocaleString::new("en", "blah1"));
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn sitelinks_misc() {
let mut i1 = Entity::new_empty_item();
let mut i2 = Entity::new_empty_item();
let params = EntityDiffParams::all();
i1.set_sitelink(SiteLink::new("enwiki", "blah1", vec![]));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.set_sitelink(SiteLink::new("enwiki", "blah1", vec![]));
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.set_sitelink(SiteLink::new("enwiki", "blah2", vec![]));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.remove_sitelink("enwiki");
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.remove_sitelink("enwiki");
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.set_sitelink(SiteLink::new("enwiki", "blah1", vec![]));
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn claims_misc() {
let mut i1 = Entity::new_empty_item();
let mut i2 = Entity::new_empty_item();
let params = EntityDiffParams::all();
i1.add_claim(claim1());
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty()); assert!(EntityDiff::new_as_result(&i1, &i2, ¶ms).is_err());
i2.add_claim(claim1());
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn claims_order() {
let mut i1 = Entity::new_empty_item();
let mut i2 = Entity::new_empty_item();
let params = EntityDiffParams::all();
i1.add_claim(claim1());
i2.add_claim(claim2());
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i2.add_claim(claim1());
assert!(!EntityDiff::new(&i1, &i2, ¶ms).is_empty());
i1.add_claim(claim2());
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
#[test]
fn entity_blank() {
let i1 = Entity::new_empty_item();
let i2 = Entity::new_empty_item();
let params = EntityDiffParams::all();
assert!(EntityDiff::new(&i1, &i2, ¶ms).is_empty());
}
}