feat: add signed integer filter condition with equal comparison

This commit is contained in:
2025-10-18 11:05:00 +02:00
parent 5abdc4e133
commit f2c7267b64
6 changed files with 255 additions and 15 deletions

View File

@@ -6,6 +6,7 @@ pub fn run(filter: &Filter) -> Result<Vec<Box<dyn ToSql>>, FailedTo> {
for condition in filter.conditions() {
match condition.2 {
Condition::Bool(value) => params.push(Box::new(value)),
Condition::SignedInt(value) => params.push(Box::new(value)),
}
}
Ok(params)
@@ -14,7 +15,6 @@ pub fn run(filter: &Filter) -> Result<Vec<Box<dyn ToSql>>, FailedTo> {
#[cfg(test)]
mod tests {
use super::*;
// Ensures that an empty vector is returned when the filter has no conditions.
#[test]
fn returns_empty_vec_for_empty_filter() {
@@ -22,7 +22,6 @@ mod tests {
let params = run(&filter).unwrap();
assert!(params.is_empty());
}
// Verifies that a single boolean condition is correctly converted to a ToSql parameter.
#[test]
fn returns_params_for_one_bool_condition() {
@@ -34,7 +33,6 @@ mod tests {
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(1))
);
}
// Checks that multiple boolean conditions are correctly converted and ordered.
#[test]
fn returns_params_for_multiple_bool_conditions() {
@@ -52,4 +50,51 @@ mod tests {
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(0))
);
}
// Verifies that a single signed integer condition is correctly converted to a ToSql parameter.
#[test]
fn returns_params_for_one_signed_int_condition() {
let filter = Filter::new().with_signed_int("level", Comparison::Equal, -5);
let params = run(&filter).unwrap();
assert_eq!(params.len(), 1);
assert_eq!(
params[0].to_sql().unwrap(),
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(-5))
);
}
// Checks that multiple signed integer conditions are correctly converted and ordered.
#[test]
fn returns_params_for_multiple_signed_int_conditions() {
let filter = Filter::new()
.with_signed_int("level", Comparison::Equal, -5)
.with_signed_int("score", Comparison::Equal, 100);
let params = run(&filter).unwrap();
assert_eq!(params.len(), 2);
assert_eq!(
params[0].to_sql().unwrap(),
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(-5))
);
assert_eq!(
params[1].to_sql().unwrap(),
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(100))
);
}
// Tests conversion for a mix of boolean and signed integer conditions.
#[test]
fn returns_params_for_mixed_conditions() {
let filter = Filter::new().with_bool("is_active", true).with_signed_int(
"level",
Comparison::Equal,
-10,
);
let params = run(&filter).unwrap();
assert_eq!(params.len(), 2);
assert_eq!(
params[0].to_sql().unwrap(),
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(1))
);
assert_eq!(
params[1].to_sql().unwrap(),
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(-10))
);
}
}

View File

@@ -5,12 +5,18 @@ const BASE_SELECT: &str = r#"SELECT * FROM entity WHERE 1=1"#;
pub fn run(filter: &Filter) -> Result<String, FailedTo> {
let mut statement = String::from(BASE_SELECT);
for (i, (name, _comparison, condition)) in filter.conditions().enumerate() {
let fragment = match *condition {
Condition::Bool(_) => format!(
let fragment = match (_comparison, condition) {
(_, Condition::Bool(_)) => format!(
" AND id IN (SELECT entity_id FROM attribute WHERE id = '{}' AND value_bool = ?{})",
name,
i + 1
),
(Comparison::Equal, Condition::SignedInt(_)) => format!(
" AND id IN (SELECT entity_id FROM attribute WHERE id = '{}' AND value_int = ?{})",
name,
i + 1
),
_ => todo!(),
};
statement.push_str(&fragment);
}
@@ -21,14 +27,12 @@ pub fn run(filter: &Filter) -> Result<String, FailedTo> {
mod tests {
use super::*;
use crate::Filter;
#[test]
fn builds_statement_with_no_conditions() {
let filter = Filter::new();
let statement = run(&filter).unwrap();
assert_eq!(statement, BASE_SELECT);
}
#[test]
fn builds_statement_with_one_bool_condition() {
let filter = Filter::new().with_bool("is_active", true);
@@ -38,7 +42,6 @@ mod tests {
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'is_active' AND value_bool = ?1)"
);
}
#[test]
fn builds_statement_with_multiple_bool_conditions() {
let filter = Filter::new()
@@ -50,4 +53,37 @@ mod tests {
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'is_active' AND value_bool = ?1) AND id IN (SELECT entity_id FROM attribute WHERE id = 'is_deleted' AND value_bool = ?2)"
);
}
#[test]
fn builds_statement_with_one_signed_int_equal_condition() {
let filter = Filter::new().with_signed_int("level", Comparison::Equal, 10);
let statement = run(&filter).unwrap();
assert_eq!(
statement,
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'level' AND value_int = ?1)"
);
}
#[test]
fn builds_statement_with_multiple_signed_int_equal_conditions() {
let filter = Filter::new()
.with_signed_int("level", Comparison::Equal, 10)
.with_signed_int("score", Comparison::Equal, 100);
let statement = run(&filter).unwrap();
assert_eq!(
statement,
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'level' AND value_int = ?1) AND id IN (SELECT entity_id FROM attribute WHERE id = 'score' AND value_int = ?2)"
);
}
#[test]
fn builds_statement_with_mixed_bool_and_signed_int_conditions() {
let filter = Filter::new().with_bool("is_active", true).with_signed_int(
"level",
Comparison::Equal,
5,
);
let statement = run(&filter).unwrap();
assert_eq!(
statement,
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'is_active' AND value_bool = ?1) AND id IN (SELECT entity_id FROM attribute WHERE id = 'level' AND value_int = ?2)"
);
}
}

View File

@@ -232,6 +232,7 @@ mod tests {
pub id: String,
pub name: String,
pub price: u64,
pub sell_trend: i64,
pub in_stock: bool,
}
impl EAV for Item {
@@ -245,6 +246,7 @@ mod tests {
.with_id(&value.id)
.with_attribute("name", value.name)
.with_attribute("price", value.price)
.with_attribute("sell_trend", value.sell_trend)
.with_attribute("in_stock", value.in_stock)
}
}
@@ -254,6 +256,9 @@ mod tests {
id: entity.id.clone(),
name: entity.unwrap("name").expect("name is always present"),
price: entity.unwrap("price").expect("price is always present"),
sell_trend: entity
.unwrap("sell_trend")
.expect("sell_trend is always present"),
in_stock: entity
.unwrap("in_stock")
.expect("in_stock is always present"),
@@ -327,6 +332,7 @@ mod tests {
id: "item-123".to_string(),
name: "Test Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item_id = item.id.clone();
@@ -337,6 +343,7 @@ mod tests {
assert_eq!(entity.class, "item");
assert_eq!(entity.value_of("name"), Some(&Value::from("Test Item")));
assert_eq!(entity.value_of("price"), Some(&Value::from(100u64)));
assert_eq!(entity.value_of("sell_trend"), Some(&Value::from(0i64)));
assert_eq!(entity.value_of("in_stock"), Some(&Value::from(true)));
}
#[test]
@@ -347,6 +354,7 @@ mod tests {
id: "item-123".to_string(),
name: "First Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item_id = item1.id.clone();
@@ -355,6 +363,7 @@ mod tests {
id: "item-123".to_string(),
name: "Second Item".to_string(),
price: 200,
sell_trend: 10,
in_stock: false,
};
let _ = catalog.upsert(item2);
@@ -362,6 +371,7 @@ mod tests {
let entity = catalog.items.get(&item_id).unwrap();
assert_eq!(entity.value_of("name"), Some(&Value::from("Second Item")));
assert_eq!(entity.value_of("price"), Some(&Value::from(200u64)));
assert_eq!(entity.value_of("sell_trend"), Some(&Value::from(10i64)));
assert_eq!(entity.value_of("in_stock"), Some(&Value::from(false)));
assert_eq!(entity.state, EntityState::Updated);
}
@@ -374,12 +384,14 @@ mod tests {
id: "item-1".to_string(),
name: "Item 1".to_string(),
price: 10,
sell_trend: 0,
in_stock: true,
},
Item {
id: "item-2".to_string(),
name: "Item 2".to_string(),
price: 20,
sell_trend: 0,
in_stock: false,
},
];
@@ -388,9 +400,13 @@ mod tests {
let entity1 = catalog.items.get("item-1").unwrap();
assert_eq!(entity1.state, EntityState::New);
assert_eq!(entity1.value_of("name"), Some(&Value::from("Item 1")));
assert_eq!(entity1.value_of("price"), Some(&Value::from(10u64)));
assert_eq!(entity1.value_of("sell_trend"), Some(&Value::from(0i64)));
let entity2 = catalog.items.get("item-2").unwrap();
assert_eq!(entity2.state, EntityState::New);
assert_eq!(entity2.value_of("name"), Some(&Value::from("Item 2")));
assert_eq!(entity2.value_of("price"), Some(&Value::from(20u64)));
assert_eq!(entity2.value_of("sell_trend"), Some(&Value::from(0i64)));
}
// ## 'get()'
#[test]
@@ -401,6 +417,7 @@ mod tests {
id: "item-123".to_string(),
name: "Test Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.upsert(item.clone());
@@ -415,6 +432,7 @@ mod tests {
id: "item-123".to_string(),
name: "Test Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.upsert(item.clone());
@@ -430,12 +448,14 @@ mod tests {
id: "item-1".to_string(),
name: "Test Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item2 = Item {
id: "item-2".to_string(),
name: "Unique Item".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let _ = catalog.upsert(item1.clone());
@@ -453,12 +473,14 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item2 = Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 250,
sell_trend: 0,
in_stock: false,
};
let _ = catalog.upsert(item1.clone());
@@ -477,6 +499,11 @@ mod tests {
.get_by_class_and_attribute("in_stock", true)
.unwrap();
assert_eq!(retrieved_by_stock, Some(item1.clone()));
// Test with i64 for sell_trend attribute
let retrieved_by_sell_trend: Option<Item> = catalog
.get_by_class_and_attribute("sell_trend", 0i64)
.unwrap();
assert_eq!(retrieved_by_sell_trend, Some(item1.clone()));
}
#[test]
fn get_by_class_and_attribute_should_return_none_if_no_match() {
@@ -486,6 +513,7 @@ mod tests {
id: "item-1".to_string(),
name: "Test Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.upsert(item.clone());
@@ -509,12 +537,14 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item2 = Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let _ = catalog.upsert(item1.clone());
@@ -546,18 +576,21 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item2 = Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let item3 = Item {
id: "item-3".to_string(),
name: "Item Three".to_string(),
price: 300,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.upsert(item1.clone());
@@ -580,6 +613,7 @@ mod tests {
id: "item-1".to_string(),
name: "Test Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.upsert(item);
@@ -612,6 +646,7 @@ mod tests {
id: "item-123".to_string(),
name: "Test Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item_id = item.id.clone();
@@ -628,6 +663,7 @@ mod tests {
id: "item-123".to_string(),
name: "Test Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.upsert(item);
@@ -653,6 +689,7 @@ mod tests {
id: "item-1".to_string(),
name: "Test Item".to_string(),
price: 123,
sell_trend: 0,
in_stock: true,
};
let _ = catalog1.upsert(item1.clone());
@@ -682,6 +719,7 @@ mod tests {
id: "item-to-delete".to_string(),
name: "Test Item".to_string(),
price: 123,
sell_trend: 0,
in_stock: true,
};
let _ = catalog1.upsert(item1.clone());
@@ -714,6 +752,7 @@ mod tests {
id: "item-1".to_string(),
name: "Original Name".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog1.upsert(original_item.clone());
@@ -726,6 +765,7 @@ mod tests {
id: "item-1".to_string(),
name: "Updated Name".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let _ = catalog2.upsert(updated_item.clone());
@@ -761,18 +801,21 @@ mod tests {
id: "update-me".to_string(),
name: "Original".to_string(),
price: 10,
sell_trend: 0,
in_stock: true,
};
let item_to_delete = Item {
id: "delete-me".to_string(),
name: "Delete Me".to_string(),
price: 20,
sell_trend: 0,
in_stock: true,
};
let item_to_keep = Item {
id: "keep-me".to_string(),
name: "Keep Me".to_string(),
price: 30,
sell_trend: 0,
in_stock: true,
};
let _ = catalog_setup.upsert(item_to_update_original.clone());
@@ -787,6 +830,7 @@ mod tests {
id: "add-me".to_string(),
name: "Add Me".to_string(),
price: 40,
sell_trend: 0,
in_stock: false,
};
let _ = catalog_ops.upsert(item_to_add.clone()); // State: New
@@ -795,6 +839,7 @@ mod tests {
id: "update-me".to_string(),
name: "Updated".to_string(),
price: 11,
sell_trend: 0,
in_stock: false,
};
let _ = catalog_ops.upsert(item_to_update_new.clone()); // State: Updated
@@ -834,6 +879,7 @@ mod tests {
id: "item-1".to_string(),
name: "Test".to_string(),
price: 10,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.upsert(item);
@@ -859,17 +905,23 @@ mod tests {
let item_to_update = Item {
id: "update-me".to_string(),
name: "Original".to_string(),
..Default::default()
price: 0,
sell_trend: 0,
in_stock: false,
};
let item_to_delete = Item {
id: "delete-me".to_string(),
name: "Delete Me".to_string(),
..Default::default()
price: 0,
sell_trend: 0,
in_stock: false,
};
let item_untouched = Item {
id: "keep-me".to_string(),
name: "Keep Me".to_string(),
..Default::default()
price: 0,
sell_trend: 0,
in_stock: false,
};
let _ = catalog.upsert(item_to_update.clone());
let _ = catalog.upsert(item_to_delete.clone());
@@ -894,14 +946,18 @@ mod tests {
let item_new = Item {
id: "add-me".to_string(),
name: "Add Me".to_string(),
..Default::default()
price: 0,
sell_trend: 0,
in_stock: false,
};
let _ = catalog.upsert(item_new.clone()); // State: New
// An updated version of an existing item.
let item_updated = Item {
id: "update-me".to_string(),
name: "Updated".to_string(),
..Default::default()
price: 0,
sell_trend: 10,
in_stock: false,
};
let _ = catalog.upsert(item_updated.clone()); // State: Updated
// An item to be deleted.
@@ -941,6 +997,10 @@ mod tests {
updated_item_entity.value_of("name"),
Some(&Value::from("Updated"))
);
assert_eq!(
updated_item_entity.value_of("sell_trend"),
Some(&Value::from(10i64))
);
let untouched_item_entity = catalog.items.get("keep-me").unwrap();
assert_eq!(untouched_item_entity.state, EntityState::Loaded);
assert_eq!(
@@ -967,6 +1027,7 @@ mod tests {
id: "item-1".to_string(),
name: "Test Item".to_string(),
price: 123,
sell_trend: 0,
in_stock: true,
};
let _ = catalog1.upsert(item_to_persist.clone());
@@ -988,6 +1049,7 @@ mod tests {
Some(&Value::from("Test Item"))
);
assert_eq!(loaded_entity.value_of("price"), Some(&Value::from(123u64)));
assert_eq!(loaded_entity.value_of("sell_trend"), Some(&Value::from(0i64)));
assert_eq!(loaded_entity.value_of("in_stock"), Some(&Value::from(true)));
assert_eq!(loaded_entity.state, EntityState::Loaded); // Should be Synced after loading.
// 6. Also verify by using the public 'get' method.
@@ -1012,6 +1074,7 @@ mod tests {
id: "item-1".to_string(),
name: "Item from DB".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog1.upsert(item_in_db.clone());
@@ -1022,6 +1085,7 @@ mod tests {
id: "item-1".to_string(),
name: "In-memory version".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let _ = catalog2.upsert(item_in_memory);
@@ -1045,6 +1109,10 @@ mod tests {
entity_after_load.value_of("price"),
Some(&Value::from(100u64))
);
assert_eq!(
entity_after_load.value_of("sell_trend"),
Some(&Value::from(0i64))
);
// 5. Verify using the public 'get' method.
let retrieved_item: Item = catalog2.get("item-1").unwrap().unwrap();
assert_eq!(retrieved_item, item_in_db);
@@ -1102,12 +1170,14 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item2 = Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let _ = catalog1.upsert(item1.clone());
@@ -1150,6 +1220,7 @@ mod tests {
id: "item-1".to_string(),
name: "DB Version".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog1.upsert(item_in_db.clone());
@@ -1160,6 +1231,7 @@ mod tests {
id: "item-1".to_string(),
name: "Memory Version".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let _ = catalog2.upsert(item_in_memory);
@@ -1203,6 +1275,7 @@ mod tests {
id: "item-1".to_string(),
name: "In-memory only".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.upsert(item_in_memory.clone());
@@ -1249,18 +1322,21 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
},
Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
},
Item {
id: "item-3".to_string(),
name: "Item Three".to_string(),
price: 300,
sell_trend: 0,
in_stock: true,
},
];
@@ -1290,12 +1366,14 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
},
Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
},
];
@@ -1325,12 +1403,14 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
},
Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
},
];
@@ -1357,6 +1437,7 @@ mod tests {
id: "item-1".to_string(),
name: "DB Version".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
catalog_setup.upsert(item_in_db.clone()).unwrap();
@@ -1366,6 +1447,7 @@ mod tests {
id: "item-1".to_string(),
name: "Memory Version".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
catalog.upsert(item_in_memory).unwrap();
@@ -1387,6 +1469,55 @@ mod tests {
assert_eq!(result.unwrap_err(), FailedTo::LoadFromDB);
std::fs::remove_dir_all(invalid_path).unwrap();
}
#[test]
fn load_by_filter_should_load_matching_sell_trend() {
let db_path = "target/test_dbs/lbf_matching_sell_trend.db";
let path = std::path::Path::new(db_path);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
if path.exists() {
std::fs::remove_file(path).unwrap();
}
let mut catalog_setup = Catalog::new(db_path);
catalog_setup.init().unwrap();
let items = vec![
Item {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 10,
in_stock: true,
},
Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: -5,
in_stock: false,
},
Item {
id: "item-3".to_string(),
name: "Item Three".to_string(),
price: 300,
sell_trend: 10,
in_stock: true,
},
];
catalog_setup.insert_many(items).unwrap();
catalog_setup.persist().unwrap();
let mut catalog = Catalog::new(db_path);
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Equal, 10);
assert!(catalog.load_by_filter(&filter).is_ok());
assert_eq!(catalog.items.len(), 2);
assert!(catalog.items.contains_key("item-1"));
assert!(catalog.items.contains_key("item-3"));
assert!(!catalog.items.contains_key("item-2"));
std::fs::remove_file(path).unwrap();
}
// ## Integration Tests
#[test]
fn integration_test_init_insert_persist_load_get() {
@@ -1404,6 +1535,7 @@ mod tests {
id: "item-1".to_string(),
name: "Integration Test Item".to_string(),
price: 999,
sell_trend: 0,
in_stock: true,
};
let _ = catalog1.upsert(item_to_insert.clone());
@@ -1434,12 +1566,14 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
},
Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
},
];
@@ -1478,6 +1612,7 @@ mod tests {
id: "item-to-delete".to_string(),
name: "Test Item".to_string(),
price: 123,
sell_trend: 0,
in_stock: true,
};
let _ = catalog1.upsert(item_to_delete.clone());
@@ -1506,12 +1641,14 @@ mod tests {
id: "item-1".to_string(),
name: "First Item".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item2 = Item {
id: "item-2".to_string(),
name: "Second Item".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let _ = catalog.upsert(item1.clone());
@@ -1532,18 +1669,21 @@ mod tests {
id: "item-1".to_string(),
name: "Item One".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let item2 = Item {
id: "item-2".to_string(),
name: "Item Two".to_string(),
price: 200,
sell_trend: 0,
in_stock: false,
};
let item3 = Item {
id: "item-3".to_string(),
name: "Item Three".to_string(),
price: 150,
sell_trend: 0,
in_stock: true,
};
let _ = catalog.insert_many(vec![item1.clone(), item2.clone(), item3.clone()]);
@@ -1579,6 +1719,7 @@ mod tests {
id: "item-1".to_string(),
name: "Original".to_string(),
price: 100,
sell_trend: 0,
in_stock: true,
};
let _ = catalog_setup.upsert(initial_item);
@@ -1611,8 +1752,8 @@ mod tests {
catalog_verify.load_by_id("item-1").unwrap();
let final_item: Item = catalog_verify.get("item-1").unwrap().unwrap();
// The final state depends on which thread persisted last. One update will have been lost.
let thread1_won = final_item.name == "Updated by Thread 1" && final_item.price == 100;
let thread2_won = final_item.name == "Original" && final_item.price == 200;
let thread1_won = final_item.name == "Updated by Thread 1" && final_item.price == 100 && final_item.sell_trend == 0;
let thread2_won = final_item.name == "Original" && final_item.price == 200 && final_item.sell_trend == 0;
assert!(
thread1_won || thread2_won,
"Final state must be the result of one of the threads winning the race."

View File

@@ -1,4 +1,8 @@
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)]
pub enum E {
Equal,
Greater,
GreaterOrEqual,
Lesser,
LesserOrEqual,
}

View File

@@ -1,4 +1,5 @@
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)]
pub enum E {
Bool(bool),
SignedInt(i64),
}

View File

@@ -19,6 +19,19 @@ impl Filter {
));
self
}
pub fn with_signed_int(
mut self,
attribute_name: &str,
comparison: Comparison,
value: i64,
) -> Self {
self.conditions.push((
attribute_name.to_string(),
comparison,
Condition::SignedInt(value),
));
self
}
pub(crate) fn conditions(&self) -> impl Iterator<Item = &(String, Comparison, Condition)> {
self.conditions.iter()
}