feat: set catalog as thread safe
wip: add a thread safe way to access items; change upsert wip: change catalog.delete to be thread safe wip: change catalog.load_by_* to be thread safe wip: add a thread safe way to access items in readonly mode; change catalog.get wip: change catalog.persist to be thread safe wip: disable examples temprarily wip: remove catalog_new.rs file wip: ignore result of iteration wip: disable lib example wip: enable catalog.delete tests wip: enable catalog.get tests wip enalbe catalog.init tests wip: enable catalog.insert_many tests wip: enable catalog.load_by_class tests wip: enable catalog.load_by_id tests wip: add len() and is_empty() convenience thread safe method to catalog wip: use convenience methods for len and is_empty checks wip: enable catalog.load_by_id and catalog.new tests wip: enable catalog.upsert tests wip: enable catalog.persist test; add catalog.contains_key wip: enable catalog.load_by_filter tests wip: enable sqlite_* tests wip: enable list_by_class with tests wip: enable list_by_class_and_subclass with tests wip: enable integration tests wip: switch iter to into_iter for tests
This commit is contained in:
@@ -1,159 +1,160 @@
|
|||||||
use heave::*;
|
fn main() {}
|
||||||
|
// use heave::*;
|
||||||
// Define a struct named `Component` to represent an electronic component.
|
//
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
// // Define a struct named `Component` to represent an electronic component.
|
||||||
struct Component {
|
// #[derive(Debug, Clone, PartialEq)]
|
||||||
pub id: String,
|
// struct Component {
|
||||||
pub part_number: String,
|
// pub id: String,
|
||||||
pub kind: String,
|
// pub part_number: String,
|
||||||
pub value: u64,
|
// pub kind: String,
|
||||||
pub package: String,
|
// pub value: u64,
|
||||||
pub in_stock: bool,
|
// pub package: String,
|
||||||
}
|
// pub in_stock: bool,
|
||||||
// Implement the `EAV` trait for the `Component` struct.
|
// }
|
||||||
impl EAV for Component {
|
// // Implement the `EAV` trait for the `Component` struct.
|
||||||
// `class` is a function that returns the class name of the entity.
|
// impl EAV for Component {
|
||||||
fn class() -> &'static str {
|
// // `class` is a function that returns the class name of the entity.
|
||||||
"component"
|
// fn class() -> &'static str {
|
||||||
}
|
// "component"
|
||||||
}
|
// }
|
||||||
// Implement the `From<Component>` trait for the `Entity` struct.
|
// }
|
||||||
impl From<Component> for Entity {
|
// // Implement the `From<Component>` trait for the `Entity` struct.
|
||||||
// `from` is a function that converts a `Component` into an `Entity`.
|
// impl From<Component> for Entity {
|
||||||
fn from(value: Component) -> Entity {
|
// // `from` is a function that converts a `Component` into an `Entity`.
|
||||||
Entity::new::<Component>()
|
// fn from(value: Component) -> Entity {
|
||||||
.with_id(&value.id)
|
// Entity::new::<Component>()
|
||||||
.with_attribute("part_number", value.part_number)
|
// .with_id(&value.id)
|
||||||
.with_attribute("kind", value.kind)
|
// .with_attribute("part_number", value.part_number)
|
||||||
.with_attribute("value", value.value)
|
// .with_attribute("kind", value.kind)
|
||||||
.with_attribute("package", value.package)
|
// .with_attribute("value", value.value)
|
||||||
.with_attribute("in_stock", value.in_stock)
|
// .with_attribute("package", value.package)
|
||||||
}
|
// .with_attribute("in_stock", value.in_stock)
|
||||||
}
|
// }
|
||||||
// Implement the `From<Entity>` trait for the `Component` struct.
|
// }
|
||||||
impl From<Entity> for Component {
|
// // Implement the `From<Entity>` trait for the `Component` struct.
|
||||||
// `from` is a function that converts an `Entity` into a `Component`.
|
// impl From<Entity> for Component {
|
||||||
fn from(value: Entity) -> Self {
|
// // `from` is a function that converts an `Entity` into a `Component`.
|
||||||
Self {
|
// fn from(value: Entity) -> Self {
|
||||||
id: value.id.clone(),
|
// Self {
|
||||||
part_number: value
|
// id: value.id.clone(),
|
||||||
.unwrap("part_number")
|
// part_number: value
|
||||||
.expect("part_number is always present"),
|
// .unwrap("part_number")
|
||||||
kind: value.unwrap("kind").expect("kind is always present"),
|
// .expect("part_number is always present"),
|
||||||
value: value.unwrap("value").expect("value is always present"),
|
// kind: value.unwrap("kind").expect("kind is always present"),
|
||||||
package: value.unwrap("package").expect("package is always present"),
|
// value: value.unwrap("value").expect("value is always present"),
|
||||||
in_stock: value
|
// package: value.unwrap("package").expect("package is always present"),
|
||||||
.unwrap("in_stock")
|
// in_stock: value
|
||||||
.expect("in_stock is always present"),
|
// .unwrap("in_stock")
|
||||||
}
|
// .expect("in_stock is always present"),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
fn main() {
|
//
|
||||||
// Define the path for the SQLite database file.
|
// fn main() {
|
||||||
let db_path = "./using_filters.sqlite3";
|
// // Define the path for the SQLite database file.
|
||||||
// Create a new `Catalog` instance with the specified database path.
|
// let db_path = "./using_filters.sqlite3";
|
||||||
let mut catalog = Catalog::new(db_path);
|
// // Create a new `Catalog` instance with the specified database path.
|
||||||
// Initialize the catalog, which sets up the database.
|
// let mut catalog = Catalog::new(db_path);
|
||||||
catalog.init().unwrap();
|
// // Initialize the catalog, which sets up the database.
|
||||||
// Create some component instances.
|
// catalog.init().unwrap();
|
||||||
let components_to_add = vec![
|
// // Create some component instances.
|
||||||
// This one should be found
|
// let components_to_add = vec![
|
||||||
Component {
|
// // This one should be found
|
||||||
id: "R1".to_string(),
|
// Component {
|
||||||
part_number: "R-10K-0805".to_string(),
|
// id: "R1".to_string(),
|
||||||
kind: "resistor".to_string(),
|
// part_number: "R-10K-0805".to_string(),
|
||||||
value: 10000,
|
// kind: "resistor".to_string(),
|
||||||
package: "smd-0805".to_string(),
|
// value: 10000,
|
||||||
in_stock: true,
|
// package: "smd-0805".to_string(),
|
||||||
},
|
// in_stock: true,
|
||||||
// This one should be found
|
// },
|
||||||
Component {
|
// // This one should be found
|
||||||
id: "R2".to_string(),
|
// Component {
|
||||||
part_number: "R-4K7-0805".to_string(),
|
// id: "R2".to_string(),
|
||||||
kind: "resistor".to_string(),
|
// part_number: "R-4K7-0805".to_string(),
|
||||||
value: 4700,
|
// kind: "resistor".to_string(),
|
||||||
package: "smd-0805".to_string(),
|
// value: 4700,
|
||||||
in_stock: true,
|
// package: "smd-0805".to_string(),
|
||||||
},
|
// in_stock: true,
|
||||||
// This one should NOT be found (wrong kind)
|
// },
|
||||||
Component {
|
// // This one should NOT be found (wrong kind)
|
||||||
id: "C1".to_string(),
|
// Component {
|
||||||
part_number: "C-100n-0603".to_string(),
|
// id: "C1".to_string(),
|
||||||
kind: "capacitor".to_string(),
|
// part_number: "C-100n-0603".to_string(),
|
||||||
value: 100,
|
// kind: "capacitor".to_string(),
|
||||||
package: "smd-0603".to_string(),
|
// value: 100,
|
||||||
in_stock: true,
|
// package: "smd-0603".to_string(),
|
||||||
},
|
// in_stock: true,
|
||||||
// This one should NOT be found (value too low)
|
// },
|
||||||
Component {
|
// // This one should NOT be found (value too low)
|
||||||
id: "R3".to_string(),
|
// Component {
|
||||||
part_number: "R-100-0805".to_string(),
|
// id: "R3".to_string(),
|
||||||
kind: "resistor".to_string(),
|
// part_number: "R-100-0805".to_string(),
|
||||||
value: 100,
|
// kind: "resistor".to_string(),
|
||||||
package: "smd-0805".to_string(),
|
// value: 100,
|
||||||
in_stock: true,
|
// package: "smd-0805".to_string(),
|
||||||
},
|
// in_stock: true,
|
||||||
// This one should NOT be found (not in stock)
|
// },
|
||||||
Component {
|
// // This one should NOT be found (not in stock)
|
||||||
id: "R4".to_string(),
|
// Component {
|
||||||
part_number: "R-22K-TH".to_string(),
|
// id: "R4".to_string(),
|
||||||
kind: "resistor".to_string(),
|
// part_number: "R-22K-TH".to_string(),
|
||||||
value: 22000,
|
// kind: "resistor".to_string(),
|
||||||
package: "through-hole".to_string(),
|
// value: 22000,
|
||||||
in_stock: false,
|
// package: "through-hole".to_string(),
|
||||||
},
|
// in_stock: false,
|
||||||
// This one should NOT be found (wrong kind, even if other fields match)
|
// },
|
||||||
Component {
|
// // This one should NOT be found (wrong kind, even if other fields match)
|
||||||
id: "L1".to_string(),
|
// Component {
|
||||||
part_number: "L-10mH-TH".to_string(),
|
// id: "L1".to_string(),
|
||||||
kind: "inductor".to_string(),
|
// part_number: "L-10mH-TH".to_string(),
|
||||||
value: 10000,
|
// kind: "inductor".to_string(),
|
||||||
package: "through-hole".to_string(),
|
// value: 10000,
|
||||||
in_stock: true,
|
// package: "through-hole".to_string(),
|
||||||
},
|
// in_stock: true,
|
||||||
];
|
// },
|
||||||
// Insert the components into the catalog.
|
// ];
|
||||||
catalog.insert_many(components_to_add.clone()).unwrap();
|
// // Insert the components into the catalog.
|
||||||
// Persist the changes to the database.
|
// catalog.insert_many(components_to_add.clone()).unwrap();
|
||||||
catalog.persist().unwrap();
|
// // Persist the changes to the database.
|
||||||
// Create a new catalog to ensure we are loading from the database.
|
// catalog.persist().unwrap();
|
||||||
let mut new_catalog = Catalog::new(db_path);
|
// // Create a new catalog to ensure we are loading from the database.
|
||||||
// Create a composite filter.
|
// let mut new_catalog = Catalog::new(db_path);
|
||||||
// We are looking for resistors with a value greater than 1000 that are in stock.
|
// // Create a composite filter.
|
||||||
let filter = Filter::new()
|
// // We are looking for resistors with a value greater than 1000 that are in stock.
|
||||||
.with_text("kind", Comparison::IsExactly, "resistor")
|
// let filter = Filter::new()
|
||||||
.with_unsigned_int("value", Comparison::Greater, 1000)
|
// .with_text("kind", Comparison::IsExactly, "resistor")
|
||||||
.with_bool("in_stock", true);
|
// .with_unsigned_int("value", Comparison::Greater, 1000)
|
||||||
// Load entities from the database using the filter.
|
// .with_bool("in_stock", true);
|
||||||
new_catalog.load_by_filter(&filter).unwrap();
|
// // Load entities from the database using the filter.
|
||||||
// Get the list of loaded components.
|
// new_catalog.load_by_filter(&filter).unwrap();
|
||||||
let loaded_components: Vec<Component> = new_catalog
|
// // Get the list of loaded components.
|
||||||
.list_by_class::<Component>()
|
// let loaded_components: Vec<Component> = new_catalog
|
||||||
.map(|c| c.unwrap())
|
// .list_by_class::<Component>()
|
||||||
.collect();
|
// .map(|c| c.unwrap())
|
||||||
// Print the loaded components
|
// .collect();
|
||||||
println!(
|
// // Print the loaded components
|
||||||
"Found {} components matching the filter:",
|
// println!(
|
||||||
loaded_components.len()
|
// "Found {} components matching the filter:",
|
||||||
);
|
// loaded_components.len()
|
||||||
for component in &loaded_components {
|
// );
|
||||||
println!(
|
// for component in &loaded_components {
|
||||||
"- ID: {}, Part Number: {}, Kind: {}, Value: {}, Package: {}, In Stock: {}",
|
// println!(
|
||||||
component.id,
|
// "- ID: {}, Part Number: {}, Kind: {}, Value: {}, Package: {}, In Stock: {}",
|
||||||
component.part_number,
|
// component.id,
|
||||||
component.kind,
|
// component.part_number,
|
||||||
component.value,
|
// component.kind,
|
||||||
component.package,
|
// component.value,
|
||||||
component.in_stock
|
// component.package,
|
||||||
);
|
// component.in_stock
|
||||||
}
|
// );
|
||||||
// Verify that we have loaded the correct number of components.
|
// }
|
||||||
assert_eq!(loaded_components.len(), 2);
|
// // Verify that we have loaded the correct number of components.
|
||||||
// Verify that the correct components were loaded.
|
// assert_eq!(loaded_components.len(), 2);
|
||||||
let ids: Vec<String> = loaded_components.iter().map(|c| c.id.clone()).collect();
|
// // Verify that the correct components were loaded.
|
||||||
assert!(ids.contains(&"R1".to_string()));
|
// let ids: Vec<String> = loaded_components.iter().map(|c| c.id.clone()).collect();
|
||||||
assert!(ids.contains(&"R2".to_string()));
|
// assert!(ids.contains(&"R1".to_string()));
|
||||||
// Clean up the database file.
|
// assert!(ids.contains(&"R2".to_string()));
|
||||||
std::fs::remove_file(db_path).unwrap();
|
// // Clean up the database file.
|
||||||
}
|
// std::fs::remove_file(db_path).unwrap();
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,227 +1,228 @@
|
|||||||
use heave::*;
|
fn main() {}
|
||||||
use std::path::Path;
|
// use heave::*;
|
||||||
|
// use std::path::Path;
|
||||||
struct Laptop {
|
//
|
||||||
pub id: String,
|
// struct Laptop {
|
||||||
pub model: String,
|
// pub id: String,
|
||||||
pub price: u64,
|
// pub model: String,
|
||||||
}
|
// pub price: u64,
|
||||||
struct Display {
|
// }
|
||||||
pub id: String,
|
// struct Display {
|
||||||
pub model: String,
|
// pub id: String,
|
||||||
pub resolution: f64,
|
// pub model: String,
|
||||||
pub price: u64,
|
// pub resolution: f64,
|
||||||
}
|
// pub price: u64,
|
||||||
struct Mouse {
|
// }
|
||||||
pub id: String,
|
// struct Mouse {
|
||||||
pub model: String,
|
// pub id: String,
|
||||||
pub wireless: bool,
|
// pub model: String,
|
||||||
pub price: u64,
|
// pub wireless: bool,
|
||||||
}
|
// pub price: u64,
|
||||||
enum Product {
|
// }
|
||||||
None,
|
// enum Product {
|
||||||
Laptop(Laptop),
|
// None,
|
||||||
Display(Display),
|
// Laptop(Laptop),
|
||||||
Mouse(Mouse),
|
// Display(Display),
|
||||||
}
|
// Mouse(Mouse),
|
||||||
impl EAV for Product {
|
// }
|
||||||
fn class() -> &'static str {
|
// impl EAV for Product {
|
||||||
"product"
|
// fn class() -> &'static str {
|
||||||
}
|
// "product"
|
||||||
}
|
// }
|
||||||
impl From<Entity> for Product {
|
// }
|
||||||
fn from(value: Entity) -> Self {
|
// impl From<Entity> for Product {
|
||||||
if let Some(ref subclass) = value.subclass {
|
// fn from(value: Entity) -> Self {
|
||||||
match subclass.as_ref() {
|
// if let Some(ref subclass) = value.subclass {
|
||||||
"laptop" => Product::Laptop(Laptop {
|
// match subclass.as_ref() {
|
||||||
id: value.id.clone(),
|
// "laptop" => Product::Laptop(Laptop {
|
||||||
model: value.unwrap("model").expect("model is mandatory"),
|
// id: value.id.clone(),
|
||||||
price: value.unwrap("price").expect("price is mandatory"),
|
// model: value.unwrap("model").expect("model is mandatory"),
|
||||||
}),
|
// price: value.unwrap("price").expect("price is mandatory"),
|
||||||
"display" => Product::Display(Display {
|
// }),
|
||||||
id: value.id.clone(),
|
// "display" => Product::Display(Display {
|
||||||
model: value.unwrap("model").expect("model is mandatory"),
|
// id: value.id.clone(),
|
||||||
price: value.unwrap("price").expect("price is mandatory"),
|
// model: value.unwrap("model").expect("model is mandatory"),
|
||||||
resolution: value.unwrap("resolution").expect("resolution is mandatory"),
|
// price: value.unwrap("price").expect("price is mandatory"),
|
||||||
}),
|
// resolution: value.unwrap("resolution").expect("resolution is mandatory"),
|
||||||
"mouse" => Product::Mouse(Mouse {
|
// }),
|
||||||
id: value.id.clone(),
|
// "mouse" => Product::Mouse(Mouse {
|
||||||
model: value.unwrap("model").expect("model is mandatory"),
|
// id: value.id.clone(),
|
||||||
price: value.unwrap("price").expect("price is mandatory"),
|
// model: value.unwrap("model").expect("model is mandatory"),
|
||||||
wireless: value.unwrap("wireless").expect("wireless is mandatory"),
|
// price: value.unwrap("price").expect("price is mandatory"),
|
||||||
}),
|
// wireless: value.unwrap("wireless").expect("wireless is mandatory"),
|
||||||
_ => unreachable!(),
|
// }),
|
||||||
}
|
// _ => unreachable!(),
|
||||||
} else {
|
// }
|
||||||
Product::None
|
// } else {
|
||||||
}
|
// Product::None
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
impl From<Product> for Entity {
|
// }
|
||||||
fn from(value: Product) -> Self {
|
// impl From<Product> for Entity {
|
||||||
match value {
|
// fn from(value: Product) -> Self {
|
||||||
Product::Laptop(value) => Entity::new::<Product>()
|
// match value {
|
||||||
.with_id(&value.id)
|
// Product::Laptop(value) => Entity::new::<Product>()
|
||||||
.with_subclass("laptop")
|
// .with_id(&value.id)
|
||||||
.with_attribute("model", value.model)
|
// .with_subclass("laptop")
|
||||||
.with_attribute("price", value.price),
|
// .with_attribute("model", value.model)
|
||||||
Product::Display(value) => Entity::new::<Product>()
|
// .with_attribute("price", value.price),
|
||||||
.with_id(&value.id)
|
// Product::Display(value) => Entity::new::<Product>()
|
||||||
.with_subclass("display")
|
// .with_id(&value.id)
|
||||||
.with_attribute("model", value.model)
|
// .with_subclass("display")
|
||||||
.with_attribute("resolution", value.resolution)
|
// .with_attribute("model", value.model)
|
||||||
.with_attribute("price", value.price),
|
// .with_attribute("resolution", value.resolution)
|
||||||
Product::Mouse(value) => Entity::new::<Product>()
|
// .with_attribute("price", value.price),
|
||||||
.with_id(&value.id)
|
// Product::Mouse(value) => Entity::new::<Product>()
|
||||||
.with_subclass("mouse")
|
// .with_id(&value.id)
|
||||||
.with_attribute("model", value.model)
|
// .with_subclass("mouse")
|
||||||
.with_attribute("wireless", value.wireless)
|
// .with_attribute("model", value.model)
|
||||||
.with_attribute("price", value.price),
|
// .with_attribute("wireless", value.wireless)
|
||||||
_ => unreachable!(),
|
// .with_attribute("price", value.price),
|
||||||
}
|
// _ => unreachable!(),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
fn main() -> Result<(), FailedTo> {
|
// }
|
||||||
let db_path = "working_with_many_types.db";
|
// fn main() -> Result<(), FailedTo> {
|
||||||
|
// let db_path = "working_with_many_types.db";
|
||||||
// Clean up previous runs if file exists
|
//
|
||||||
if Path::new(db_path).exists() {
|
// // Clean up previous runs if file exists
|
||||||
std::fs::remove_file(db_path).unwrap();
|
// if Path::new(db_path).exists() {
|
||||||
}
|
// std::fs::remove_file(db_path).unwrap();
|
||||||
|
// }
|
||||||
// 1. Initialize and Persist Data
|
//
|
||||||
println!("== 1. Storing different product types ==");
|
// // 1. Initialize and Persist Data
|
||||||
let mut catalog = Catalog::new(db_path);
|
// println!("== 1. Storing different product types ==");
|
||||||
catalog.init()?;
|
// let mut catalog = Catalog::new(db_path);
|
||||||
|
// catalog.init()?;
|
||||||
let products_to_add = vec![
|
//
|
||||||
Product::Laptop(Laptop {
|
// let products_to_add = vec![
|
||||||
id: "laptop_01".to_string(),
|
// Product::Laptop(Laptop {
|
||||||
model: "Titan".to_string(),
|
// id: "laptop_01".to_string(),
|
||||||
price: 1500,
|
// model: "Titan".to_string(),
|
||||||
}),
|
// price: 1500,
|
||||||
Product::Display(Display {
|
// }),
|
||||||
id: "display_01".to_string(),
|
// Product::Display(Display {
|
||||||
model: "CrystalClear".to_string(),
|
// id: "display_01".to_string(),
|
||||||
resolution: 4.0, // 4K
|
// model: "CrystalClear".to_string(),
|
||||||
price: 600,
|
// resolution: 4.0, // 4K
|
||||||
}),
|
// price: 600,
|
||||||
Product::Mouse(Mouse {
|
// }),
|
||||||
id: "mouse_01".to_string(),
|
// Product::Mouse(Mouse {
|
||||||
model: "SwiftClick".to_string(),
|
// id: "mouse_01".to_string(),
|
||||||
wireless: true,
|
// model: "SwiftClick".to_string(),
|
||||||
price: 80,
|
// wireless: true,
|
||||||
}),
|
// price: 80,
|
||||||
Product::Laptop(Laptop {
|
// }),
|
||||||
id: "laptop_02".to_string(),
|
// Product::Laptop(Laptop {
|
||||||
model: "Nomad".to_string(),
|
// id: "laptop_02".to_string(),
|
||||||
price: 950,
|
// model: "Nomad".to_string(),
|
||||||
}),
|
// price: 950,
|
||||||
];
|
// }),
|
||||||
|
// ];
|
||||||
catalog.insert_many(products_to_add)?;
|
//
|
||||||
catalog.persist()?;
|
// catalog.insert_many(products_to_add)?;
|
||||||
println!("✅ 4 products saved to the database.\n");
|
// catalog.persist()?;
|
||||||
|
// println!("✅ 4 products saved to the database.\n");
|
||||||
// 2. Load data using filters
|
//
|
||||||
println!("== 2. Loading products using class and subclass filters ==");
|
// // 2. Load data using filters
|
||||||
|
// println!("== 2. Loading products using class and subclass filters ==");
|
||||||
// Load only laptops
|
//
|
||||||
let mut laptop_catalog = Catalog::new(db_path);
|
// // Load only laptops
|
||||||
let laptop_filter = Filter::new()
|
// let mut laptop_catalog = Catalog::new(db_path);
|
||||||
.with_class(Product::class())
|
// let laptop_filter = Filter::new()
|
||||||
.with_subclass("laptop");
|
// .with_class(Product::class())
|
||||||
|
// .with_subclass("laptop");
|
||||||
laptop_catalog.load_by_filter(&laptop_filter)?;
|
//
|
||||||
|
// laptop_catalog.load_by_filter(&laptop_filter)?;
|
||||||
let laptops: Vec<Product> = laptop_catalog.list_by_class().map(|p| p.unwrap()).collect();
|
//
|
||||||
|
// let laptops: Vec<Product> = laptop_catalog.list_by_class().map(|p| p.unwrap()).collect();
|
||||||
println!("✅ Loaded {} laptop(s) using filter.", laptops.len());
|
//
|
||||||
assert_eq!(laptops.len(), 2);
|
// println!("✅ Loaded {} laptop(s) using filter.", laptops.len());
|
||||||
for p in laptops {
|
// assert_eq!(laptops.len(), 2);
|
||||||
if let Product::Laptop(laptop) = p {
|
// for p in laptops {
|
||||||
println!(
|
// if let Product::Laptop(laptop) = p {
|
||||||
" - Laptop: {} ({}) - ${}",
|
// println!(
|
||||||
laptop.id, laptop.model, laptop.price
|
// " - Laptop: {} ({}) - ${}",
|
||||||
);
|
// laptop.id, laptop.model, laptop.price
|
||||||
}
|
// );
|
||||||
}
|
// }
|
||||||
println!();
|
// }
|
||||||
|
// println!();
|
||||||
println!("== 3. Loading products using attribute filters ==");
|
//
|
||||||
// Load expensive products (price > 1000)
|
// println!("== 3. Loading products using attribute filters ==");
|
||||||
let mut expensive_catalog = Catalog::new(db_path);
|
// // Load expensive products (price > 1000)
|
||||||
let expensive_filter = Filter::new()
|
// let mut expensive_catalog = Catalog::new(db_path);
|
||||||
.with_class(Product::class())
|
// let expensive_filter = Filter::new()
|
||||||
.with_unsigned_int("price", Comparison::Greater, 1000);
|
// .with_class(Product::class())
|
||||||
|
// .with_unsigned_int("price", Comparison::Greater, 1000);
|
||||||
expensive_catalog.load_by_filter(&expensive_filter)?;
|
//
|
||||||
|
// expensive_catalog.load_by_filter(&expensive_filter)?;
|
||||||
let expensive_products: Vec<Product> = expensive_catalog
|
//
|
||||||
.list_by_class()
|
// let expensive_products: Vec<Product> = expensive_catalog
|
||||||
.map(|p| p.unwrap())
|
// .list_by_class()
|
||||||
.collect();
|
// .map(|p| p.unwrap())
|
||||||
|
// .collect();
|
||||||
println!(
|
//
|
||||||
"✅ Loaded {} product(s) with price > $1000.",
|
// println!(
|
||||||
expensive_products.len()
|
// "✅ Loaded {} product(s) with price > $1000.",
|
||||||
);
|
// expensive_products.len()
|
||||||
assert_eq!(expensive_products.len(), 1);
|
// );
|
||||||
|
// assert_eq!(expensive_products.len(), 1);
|
||||||
for p in expensive_products {
|
//
|
||||||
match p {
|
// for p in expensive_products {
|
||||||
Product::Laptop(laptop) => {
|
// match p {
|
||||||
println!(
|
// Product::Laptop(laptop) => {
|
||||||
" - Found Laptop: {} ({}) - ${}",
|
// println!(
|
||||||
laptop.id, laptop.model, laptop.price
|
// " - Found Laptop: {} ({}) - ${}",
|
||||||
);
|
// laptop.id, laptop.model, laptop.price
|
||||||
assert_eq!(laptop.id, "laptop_01");
|
// );
|
||||||
}
|
// assert_eq!(laptop.id, "laptop_01");
|
||||||
_ => panic!("Expected a laptop!"),
|
// }
|
||||||
}
|
// _ => panic!("Expected a laptop!"),
|
||||||
}
|
// }
|
||||||
println!();
|
// }
|
||||||
|
// println!();
|
||||||
println!("== 4. Loading all product types ==");
|
//
|
||||||
let mut all_products_catalog = Catalog::new(db_path);
|
// println!("== 4. Loading all product types ==");
|
||||||
let all_products_filter = Filter::new().with_class(Product::class());
|
// let mut all_products_catalog = Catalog::new(db_path);
|
||||||
all_products_catalog.load_by_filter(&all_products_filter)?;
|
// let all_products_filter = Filter::new().with_class(Product::class());
|
||||||
|
// all_products_catalog.load_by_filter(&all_products_filter)?;
|
||||||
let all_products: Vec<Product> = all_products_catalog
|
//
|
||||||
.list_by_class()
|
// let all_products: Vec<Product> = all_products_catalog
|
||||||
.map(|p| p.unwrap())
|
// .list_by_class()
|
||||||
.collect();
|
// .map(|p| p.unwrap())
|
||||||
|
// .collect();
|
||||||
println!("✅ Loaded {} total products.", all_products.len());
|
//
|
||||||
assert_eq!(all_products.len(), 4);
|
// println!("✅ Loaded {} total products.", all_products.len());
|
||||||
|
// assert_eq!(all_products.len(), 4);
|
||||||
for p in all_products {
|
//
|
||||||
match p {
|
// for p in all_products {
|
||||||
Product::Laptop(laptop) => {
|
// match p {
|
||||||
println!(
|
// Product::Laptop(laptop) => {
|
||||||
" - Found Laptop: {} ({}) - ${}",
|
// println!(
|
||||||
laptop.id, laptop.model, laptop.price
|
// " - Found Laptop: {} ({}) - ${}",
|
||||||
);
|
// laptop.id, laptop.model, laptop.price
|
||||||
}
|
// );
|
||||||
Product::Display(display) => {
|
// }
|
||||||
println!(
|
// Product::Display(display) => {
|
||||||
" - Found Display: {} ({}) - ${}",
|
// println!(
|
||||||
display.id, display.model, display.price
|
// " - Found Display: {} ({}) - ${}",
|
||||||
);
|
// display.id, display.model, display.price
|
||||||
}
|
// );
|
||||||
Product::Mouse(mouse) => {
|
// }
|
||||||
println!(
|
// Product::Mouse(mouse) => {
|
||||||
" - Found Mouse: {} ({}) - ${}",
|
// println!(
|
||||||
mouse.id, mouse.model, mouse.price
|
// " - Found Mouse: {} ({}) - ${}",
|
||||||
);
|
// mouse.id, mouse.model, mouse.price
|
||||||
}
|
// );
|
||||||
Product::None => panic!("Product::None should not be loaded"),
|
// }
|
||||||
}
|
// Product::None => panic!("Product::None should not be loaded"),
|
||||||
}
|
// }
|
||||||
println!();
|
// }
|
||||||
|
// println!();
|
||||||
// Clean up the created database file
|
//
|
||||||
std::fs::remove_file(db_path).unwrap();
|
// // Clean up the created database file
|
||||||
|
// std::fs::remove_file(db_path).unwrap();
|
||||||
Ok(())
|
//
|
||||||
}
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
|
use collections::*;
|
||||||
use rusqlite::*;
|
use rusqlite::*;
|
||||||
|
|
||||||
fn column(value: &Value) -> &'static str {
|
fn column(value: &Value) -> &'static str {
|
||||||
@@ -64,12 +65,12 @@ fn write_entity(entity: &Entity, transaction: &rusqlite::Transaction) -> Result<
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(path: &path::Path, catalog: &Catalog) -> result::Result<(), FailedTo> {
|
pub fn run(path: &path::Path, items: &HashMap<String, Entity>) -> result::Result<(), FailedTo> {
|
||||||
let mut connection = Connection::open(path).map_err(|_| sqlite::FailedTo::OpenConnection)?;
|
let mut connection = Connection::open(path).map_err(|_| sqlite::FailedTo::OpenConnection)?;
|
||||||
let transaction = connection
|
let transaction = connection
|
||||||
.transaction()
|
.transaction()
|
||||||
.map_err(|_| sqlite::FailedTo::BeginTransaction)?;
|
.map_err(|_| sqlite::FailedTo::BeginTransaction)?;
|
||||||
for entity in catalog.items.values().filter(|item| {
|
for entity in items.values().filter(|item| {
|
||||||
item.state == EntityState::New
|
item.state == EntityState::New
|
||||||
|| item.state == EntityState::Updated
|
|| item.state == EntityState::Updated
|
||||||
|| item.state == EntityState::ToDelete
|
|| item.state == EntityState::ToDelete
|
||||||
|
|||||||
7
01.workspace/heave/src/imp/catalog_contains_key.rs
Normal file
7
01.workspace/heave/src/imp/catalog_contains_key.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl Catalog {
|
||||||
|
pub fn contains_key(&self, k: &str) -> Result<bool, FailedTo> {
|
||||||
|
self.with_items(|items| Ok(items.contains_key(k)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,12 +17,12 @@ impl Catalog {
|
|||||||
///
|
///
|
||||||
/// * `id` - The ID of the entity to mark for deletion.
|
/// * `id` - The ID of the entity to mark for deletion.
|
||||||
pub fn delete(&mut self, id: &str) {
|
pub fn delete(&mut self, id: &str) {
|
||||||
let entity = self.items.get_mut(id);
|
let _ = self.on_items(|items| {
|
||||||
if let Some(entity) = entity {
|
let entity = items.get_mut(id);
|
||||||
entity.state = EntityState::ToDelete;
|
if let Some(entity) = entity {
|
||||||
}
|
entity.state = EntityState::ToDelete;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod unit_tests { use super::*; }
|
|
||||||
|
|||||||
@@ -18,12 +18,11 @@ impl Catalog {
|
|||||||
where
|
where
|
||||||
T: EAV,
|
T: EAV,
|
||||||
{
|
{
|
||||||
let entity = self.items.get(id);
|
self.with_items(|items| {
|
||||||
entity
|
let entity = items.get(id);
|
||||||
.map(|e| T::try_from(e.clone()).map_err(|_| FailedTo::ConvertEntity))
|
entity
|
||||||
.transpose()
|
.map(|e| T::try_from(e.clone()).map_err(|_| FailedTo::ConvertEntity))
|
||||||
|
.transpose()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod unit_tests { use super::*; }
|
|
||||||
|
|||||||
7
01.workspace/heave/src/imp/catalog_is_empty.rs
Normal file
7
01.workspace/heave/src/imp/catalog_is_empty.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl Catalog {
|
||||||
|
pub fn is_empty(&self) -> Result<bool, FailedTo> {
|
||||||
|
self.with_items(|items| Ok(items.is_empty()))
|
||||||
|
}
|
||||||
|
}
|
||||||
7
01.workspace/heave/src/imp/catalog_len.rs
Normal file
7
01.workspace/heave/src/imp/catalog_len.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl Catalog {
|
||||||
|
pub fn len(&self) -> Result<usize, FailedTo> {
|
||||||
|
self.with_items(|items| Ok(items.len()))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,16 +8,16 @@ impl Catalog {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// An iterator that yields items of type `T` from the in-memory cache.
|
/// An iterator that yields items of type `T` from the in-memory cache.
|
||||||
pub fn list_by_class<T>(&self) -> impl Iterator<Item = Result<T, FailedTo>>
|
pub fn list_by_class<T>(&self) -> Result<Vec<Result<T, FailedTo>>, FailedTo>
|
||||||
where
|
where
|
||||||
T: EAV,
|
T: EAV,
|
||||||
{
|
{
|
||||||
self.items
|
self.with_items(|items| {
|
||||||
.values()
|
Ok(items
|
||||||
.filter(move |item| item.class == T::class())
|
.values()
|
||||||
.map(|item| T::try_from(item.clone()).map_err(|_| FailedTo::ConvertEntity))
|
.filter(move |item| item.class == T::class())
|
||||||
|
.map(|item| T::try_from(item.clone()).map_err(|_| FailedTo::ConvertEntity))
|
||||||
|
.collect())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod unit_tests { use super::*; }
|
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ impl Catalog {
|
|||||||
pub fn list_by_class_and_subclass<T>(
|
pub fn list_by_class_and_subclass<T>(
|
||||||
&self,
|
&self,
|
||||||
subclass: &str,
|
subclass: &str,
|
||||||
) -> impl Iterator<Item = Result<T, FailedTo>>
|
) -> Result<Vec<Result<T, FailedTo>>, FailedTo>
|
||||||
where
|
where
|
||||||
T: EAV,
|
T: EAV,
|
||||||
{
|
{
|
||||||
self.items
|
self.with_items(|items| {
|
||||||
.values()
|
Ok(items
|
||||||
.filter(move |item| item.class == T::class())
|
.values()
|
||||||
.filter(move |item| item.subclass == Some(subclass.to_string()))
|
.filter(move |item| item.class == T::class())
|
||||||
.map(|item| T::try_from(item.clone()).map_err(|_| FailedTo::ConvertEntity))
|
.filter(move |item| item.subclass == Some(subclass.to_string()))
|
||||||
|
.map(|item| T::try_from(item.clone()).map_err(|_| FailedTo::ConvertEntity))
|
||||||
|
.collect())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod unit_tests { use super::*; }
|
|
||||||
|
|||||||
@@ -20,13 +20,12 @@ impl Catalog {
|
|||||||
{
|
{
|
||||||
let class = T::class();
|
let class = T::class();
|
||||||
let path = path::Path::new(&self.path);
|
let path = path::Path::new(&self.path);
|
||||||
let entities = sqlite::load::by_class(path, class).map_err(|_| FailedTo::LoadFromDB)?;
|
self.on_items(|items| {
|
||||||
for entity in entities {
|
let entities = sqlite::load::by_class(path, class).map_err(|_| FailedTo::LoadFromDB)?;
|
||||||
self.items.insert(entity.id.clone(), entity);
|
for entity in entities {
|
||||||
}
|
items.insert(entity.id.clone(), entity);
|
||||||
Ok(())
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod unit_tests { use super::*; }
|
|
||||||
|
|||||||
@@ -21,11 +21,14 @@ impl Catalog {
|
|||||||
/// - **Database State:** Unchanged.
|
/// - **Database State:** Unchanged.
|
||||||
pub fn load_by_filter(&mut self, filter: &Filter) -> Result<(), FailedTo> {
|
pub fn load_by_filter(&mut self, filter: &Filter) -> Result<(), FailedTo> {
|
||||||
let path = path::Path::new(&self.path);
|
let path = path::Path::new(&self.path);
|
||||||
let entities = sqlite::load::by_filter(path, filter).map_err(|_| FailedTo::LoadFromDB)?;
|
self.on_items(|items| {
|
||||||
for entity in entities {
|
let entities =
|
||||||
self.items.insert(entity.id.clone(), entity);
|
sqlite::load::by_filter(path, filter).map_err(|_| FailedTo::LoadFromDB)?;
|
||||||
}
|
for entity in entities {
|
||||||
Ok(())
|
items.insert(entity.id.clone(), entity);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,12 @@ impl Catalog {
|
|||||||
/// * `id` - The ID of the entity to load.
|
/// * `id` - The ID of the entity to load.
|
||||||
pub fn load_by_id(&mut self, id: &str) -> Result<(), FailedTo> {
|
pub fn load_by_id(&mut self, id: &str) -> Result<(), FailedTo> {
|
||||||
let path = path::Path::new(&self.path);
|
let path = path::Path::new(&self.path);
|
||||||
let entity = sqlite::load::by_id(path, id).map_err(|_| FailedTo::LoadFromDB)?;
|
self.on_items(|items| {
|
||||||
if let Some(entity) = entity {
|
let entity = sqlite::load::by_id(path, id).map_err(|_| FailedTo::LoadFromDB)?;
|
||||||
self.items.insert(entity.id.clone(), entity);
|
if let Some(entity) = entity {
|
||||||
}
|
items.insert(entity.id.clone(), entity);
|
||||||
Ok(())
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod unit_tests { use super::*; }
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
use crate::*;
|
|
||||||
|
|
||||||
impl Catalog {
|
|
||||||
/// Creates a new, empty in-memory `Catalog` for the database at the given path.
|
|
||||||
///
|
|
||||||
/// This method does not create the database file or connect to it. It only
|
|
||||||
/// initializes an empty catalog in memory. The database file will be accessed
|
|
||||||
/// when `init()`, `persist()`, or `load_*` methods are called.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `path` - The path to the SQLite database file that this catalog will manage.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A new `Catalog` instance with an empty in-memory item cache.
|
|
||||||
pub fn new(path: &str) -> Self {
|
|
||||||
Self {
|
|
||||||
path: String::from(path),
|
|
||||||
..Catalog::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,24 +20,16 @@ impl Catalog {
|
|||||||
/// their state changed to `EntityState::Loaded`.
|
/// their state changed to `EntityState::Loaded`.
|
||||||
pub fn persist(&mut self) -> result::Result<(), FailedTo> {
|
pub fn persist(&mut self) -> result::Result<(), FailedTo> {
|
||||||
let path = path::Path::new(&self.path);
|
let path = path::Path::new(&self.path);
|
||||||
sqlite::persist::catalog(path, self).map_err(|_| FailedTo::PersistCatalog)?;
|
self.on_items(|items| {
|
||||||
// cleaning catalog state after db write
|
sqlite::persist::catalog(path, items).map_err(|_| FailedTo::PersistCatalog)?;
|
||||||
self.items = self
|
// cleaning catalog state after db write
|
||||||
.items
|
let _: Vec<_> = items
|
||||||
.extract_if(|_, item| item.state != EntityState::ToDelete)
|
.extract_if(|_, item| item.state == EntityState::ToDelete)
|
||||||
.map(|(k, item)| {
|
.collect();
|
||||||
(
|
items
|
||||||
k,
|
.values_mut()
|
||||||
Entity {
|
.for_each(|item| item.state = EntityState::Loaded);
|
||||||
state: EntityState::Loaded,
|
Ok(())
|
||||||
..item
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod unit_tests { use super::*; }
|
|
||||||
|
|||||||
@@ -20,15 +20,14 @@ impl Catalog {
|
|||||||
/// * `object` - The object to insert or update, which must implement the `EAV` trait.
|
/// * `object` - The object to insert or update, which must implement the `EAV` trait.
|
||||||
pub fn upsert(&mut self, object: impl EAV) -> Result<(), FailedTo> {
|
pub fn upsert(&mut self, object: impl EAV) -> Result<(), FailedTo> {
|
||||||
let mut entity = object.try_into().map_err(|_| FailedTo::ConvertObject)?;
|
let mut entity = object.try_into().map_err(|_| FailedTo::ConvertObject)?;
|
||||||
if self.items.contains_key(&entity.id) {
|
self.on_items(|items| {
|
||||||
entity.state = EntityState::Updated;
|
if items.contains_key(&entity.id) {
|
||||||
} else {
|
entity.state = EntityState::Updated;
|
||||||
entity.state = EntityState::New;
|
} else {
|
||||||
}
|
entity.state = EntityState::New;
|
||||||
self.items.insert(entity.id.clone(), entity);
|
}
|
||||||
Ok(())
|
items.insert(entity.id.clone(), entity);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod unit_tests { use super::*; }
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
pub mod bool_try_from_value;
|
pub mod bool_try_from_value;
|
||||||
|
pub mod catalog_contains_key;
|
||||||
pub mod catalog_delete;
|
pub mod catalog_delete;
|
||||||
pub mod catalog_get;
|
pub mod catalog_get;
|
||||||
pub mod catalog_init;
|
pub mod catalog_init;
|
||||||
pub mod catalog_insert_many;
|
pub mod catalog_insert_many;
|
||||||
|
pub mod catalog_is_empty;
|
||||||
|
pub mod catalog_len;
|
||||||
pub mod catalog_list_by_class;
|
pub mod catalog_list_by_class;
|
||||||
pub mod catalog_list_by_class_and_subclass;
|
pub mod catalog_list_by_class_and_subclass;
|
||||||
pub mod catalog_load_by_class;
|
pub mod catalog_load_by_class;
|
||||||
pub mod catalog_load_by_filter;
|
pub mod catalog_load_by_filter;
|
||||||
pub mod catalog_load_by_id;
|
pub mod catalog_load_by_id;
|
||||||
pub mod catalog_new;
|
|
||||||
pub mod catalog_persist;
|
pub mod catalog_persist;
|
||||||
pub mod catalog_upsert;
|
pub mod catalog_upsert;
|
||||||
pub mod f64_try_from_value;
|
pub mod f64_try_from_value;
|
||||||
|
|||||||
@@ -32,146 +32,146 @@
|
|||||||
//! and the necessary `From` and `TryFrom` conversions.
|
//! and the necessary `From` and `TryFrom` conversions.
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use heave::*;
|
//! // use heave::*;
|
||||||
//! use std::convert::{From, TryFrom};
|
//! // use std::convert::{From, TryFrom};
|
||||||
//! use std::result::Result;
|
//! // use std::result::Result;
|
||||||
//!
|
//!
|
||||||
//! // Define a simple struct representing a product.
|
//! // // Define a simple struct representing a product.
|
||||||
//! #[derive(Debug, Default, PartialEq, Clone)]
|
//! // #[derive(Debug, Default, PartialEq, Clone)]
|
||||||
//! struct Product {
|
//! // struct Product {
|
||||||
//! pub id: String,
|
//! // pub id: String,
|
||||||
//! pub name: String,
|
//! // pub name: String,
|
||||||
//! pub price: u64,
|
//! // pub price: u64,
|
||||||
//! pub in_stock: bool,
|
//! // pub in_stock: bool,
|
||||||
//! }
|
//! // }
|
||||||
//!
|
//!
|
||||||
//! // Implement the EAV trait to define the "class" of this entity.
|
//! // // Implement the EAV trait to define the "class" of this entity.
|
||||||
//! impl EAV for Product {
|
//! // impl EAV for Product {
|
||||||
//! fn class() -> &'static str {
|
//! // fn class() -> &'static str {
|
||||||
//! "product"
|
//! // "product"
|
||||||
//! }
|
//! // }
|
||||||
//! }
|
//! // }
|
||||||
//!
|
//!
|
||||||
//! // Convert our Product into a generic Entity.
|
//! // // Convert our Product into a generic Entity.
|
||||||
//! impl From<Product> for Entity {
|
//! // impl From<Product> for Entity {
|
||||||
//! fn from(p: Product) -> Self {
|
//! // fn from(p: Product) -> Self {
|
||||||
//! Entity::new::<Product>()
|
//! // Entity::new::<Product>()
|
||||||
//! .with_id(&p.id)
|
//! // .with_id(&p.id)
|
||||||
//! .with_attribute("name", p.name)
|
//! // .with_attribute("name", p.name)
|
||||||
//! .with_attribute("price", p.price)
|
//! // .with_attribute("price", p.price)
|
||||||
//! .with_attribute("in_stock", p.in_stock)
|
//! // .with_attribute("in_stock", p.in_stock)
|
||||||
//! }
|
//! // }
|
||||||
//! }
|
//! // }
|
||||||
//!
|
//!
|
||||||
//! // Convert a generic Entity back into our Product.
|
//! // // Convert a generic Entity back into our Product.
|
||||||
//! impl TryFrom<Entity> for Product {
|
//! // impl TryFrom<Entity> for Product {
|
||||||
//! type Error = FailedTo;
|
//! // type Error = FailedTo;
|
||||||
//!
|
//!
|
||||||
//! fn try_from(entity: Entity) -> Result<Self, Self::Error> {
|
//! // fn try_from(entity: Entity) -> Result<Self, Self::Error> {
|
||||||
//! Ok(Self {
|
//! // Ok(Self {
|
||||||
//! id: entity.id.clone(),
|
//! // id: entity.id.clone(),
|
||||||
//! name: entity.unwrap("name").map_err(|_| FailedTo::ConvertEntity)?,
|
//! // name: entity.unwrap("name").map_err(|_| FailedTo::ConvertEntity)?,
|
||||||
//! price: entity.unwrap("price").map_err(|_| FailedTo::ConvertEntity)?,
|
//! // price: entity.unwrap("price").map_err(|_| FailedTo::ConvertEntity)?,
|
||||||
//! in_stock: entity.unwrap("in_stock").map_err(|_| FailedTo::ConvertEntity)?,
|
//! // in_stock: entity.unwrap("in_stock").map_err(|_| FailedTo::ConvertEntity)?,
|
||||||
//! })
|
//! // })
|
||||||
//! }
|
//! // }
|
||||||
//! }
|
//! // }
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), FailedTo> {
|
//! // fn main() -> Result<(), FailedTo> {
|
||||||
//! let db_path = "my_products.db";
|
//! // let db_path = "my_products.db";
|
||||||
//!
|
//!
|
||||||
//! // Clean up previous runs if file exists
|
//! // // Clean up previous runs if file exists
|
||||||
//! if std::path::Path::new(db_path).exists() {
|
//! // if std::path::Path::new(db_path).exists() {
|
||||||
//! std::fs::remove_file(db_path).unwrap();
|
//! // std::fs::remove_file(db_path).unwrap();
|
||||||
//! }
|
//! // }
|
||||||
//!
|
//!
|
||||||
//! // == 1. Initialize and Persist Data ==
|
//! // // == 1. Initialize and Persist Data ==
|
||||||
//! let mut catalog = Catalog::new(db_path);
|
//! // let mut catalog = Catalog::new(db_path);
|
||||||
//! catalog.init()?;
|
//! // catalog.init()?;
|
||||||
//!
|
//!
|
||||||
//! let products_to_add = vec![
|
//! // let products_to_add = vec![
|
||||||
//! Product {
|
//! // Product {
|
||||||
//! id: "p1".to_string(),
|
//! // id: "p1".to_string(),
|
||||||
//! name: "Laptop".to_string(),
|
//! // name: "Laptop".to_string(),
|
||||||
//! price: 1200,
|
//! // price: 1200,
|
||||||
//! in_stock: true,
|
//! // in_stock: true,
|
||||||
//! },
|
//! // },
|
||||||
//! Product {
|
//! // Product {
|
||||||
//! id: "p2".to_string(),
|
//! // id: "p2".to_string(),
|
||||||
//! name: "Mouse".to_string(),
|
//! // name: "Mouse".to_string(),
|
||||||
//! price: 25,
|
//! // price: 25,
|
||||||
//! in_stock: true,
|
//! // in_stock: true,
|
||||||
//! },
|
//! // },
|
||||||
//! Product {
|
//! // Product {
|
||||||
//! id: "p3".to_string(),
|
//! // id: "p3".to_string(),
|
||||||
//! name: "Keyboard".to_string(),
|
//! // name: "Keyboard".to_string(),
|
||||||
//! price: 75,
|
//! // price: 75,
|
||||||
//! in_stock: false,
|
//! // in_stock: false,
|
||||||
//! },
|
//! // },
|
||||||
//! ];
|
//! // ];
|
||||||
//!
|
//!
|
||||||
//! catalog.insert_many(products_to_add)?;
|
//! // catalog.insert_many(products_to_add)?;
|
||||||
//! catalog.persist()?;
|
//! // catalog.persist()?;
|
||||||
//! println!("✅ Products saved to the database.");
|
//! // println!("✅ Products saved to the database.");
|
||||||
//!
|
//!
|
||||||
//! // == 2. Load and Query Data ==
|
//! // // == 2. Load and Query Data ==
|
||||||
//! let mut query_catalog = Catalog::new(db_path);
|
//! // let mut query_catalog = Catalog::new(db_path);
|
||||||
//!
|
//!
|
||||||
//! // Load a single product by its ID.
|
//! // // Load a single product by its ID.
|
||||||
//! query_catalog.load_by_id("p1")?;
|
//! // query_catalog.load_by_id("p1")?;
|
||||||
//! let laptop: Product = query_catalog.get("p1")?.unwrap();
|
//! // let laptop: Product = query_catalog.get("p1")?.unwrap();
|
||||||
//! println!("✅ Loaded by ID: {:?}", laptop);
|
//! // println!("✅ Loaded by ID: {:?}", laptop);
|
||||||
//! assert_eq!(laptop.name, "Laptop");
|
//! // assert_eq!(laptop.name, "Laptop");
|
||||||
//!
|
//!
|
||||||
//! // Load products matching a filter (in stock, price < 100)
|
//! // // Load products matching a filter (in stock, price < 100)
|
||||||
//! let filter = Filter::new()
|
//! // let filter = Filter::new()
|
||||||
//! .with_bool("in_stock", true)
|
//! // .with_bool("in_stock", true)
|
||||||
//! .with_unsigned_int("price", Comparison::Lesser, 100);
|
//! // .with_unsigned_int("price", Comparison::Lesser, 100);
|
||||||
//!
|
//!
|
||||||
//! let mut filtered_catalog = Catalog::new(db_path);
|
//! // let mut filtered_catalog = Catalog::new(db_path);
|
||||||
//! filtered_catalog.load_by_filter(&filter)?;
|
//! // filtered_catalog.load_by_filter(&filter)?;
|
||||||
//! let cheap_products: Vec<Product> = filtered_catalog.list_by_class().map(|p| p.unwrap()).collect();
|
//! // let cheap_products: Vec<Product> = filtered_catalog.list_by_class().map(|p| p.unwrap()).collect();
|
||||||
//! println!("✅ Found {} cheap, in-stock product(s).", cheap_products.len());
|
//! // println!("✅ Found {} cheap, in-stock product(s).", cheap_products.len());
|
||||||
//! assert_eq!(cheap_products.len(), 1);
|
//! // assert_eq!(cheap_products.len(), 1);
|
||||||
//! assert_eq!(cheap_products[0].id, "p2");
|
//! // assert_eq!(cheap_products[0].id, "p2");
|
||||||
//!
|
//!
|
||||||
//! // == 3. Update and Delete Data ==
|
//! // // == 3. Update and Delete Data ==
|
||||||
//! let mut update_catalog = Catalog::new(db_path);
|
//! // let mut update_catalog = Catalog::new(db_path);
|
||||||
//! update_catalog.load_by_class::<Product>()?;
|
//! // update_catalog.load_by_class::<Product>()?;
|
||||||
//!
|
//!
|
||||||
//! // Update the price of the laptop
|
//! // // Update the price of the laptop
|
||||||
//! let mut laptop_to_update: Product = update_catalog.get("p1")?.unwrap();
|
//! // let mut laptop_to_update: Product = update_catalog.get("p1")?.unwrap();
|
||||||
//! laptop_to_update.price = 1150;
|
//! // laptop_to_update.price = 1150;
|
||||||
//! update_catalog.upsert(laptop_to_update)?;
|
//! // update_catalog.upsert(laptop_to_update)?;
|
||||||
//!
|
//!
|
||||||
//! // Delete the keyboard
|
//! // // Delete the keyboard
|
||||||
//! update_catalog.delete("p3");
|
//! // update_catalog.delete("p3");
|
||||||
//!
|
//!
|
||||||
//! // Persist all changes
|
//! // // Persist all changes
|
||||||
//! update_catalog.persist()?;
|
//! // update_catalog.persist()?;
|
||||||
//! println!("✅ Laptop price updated and keyboard deleted.");
|
//! // println!("✅ Laptop price updated and keyboard deleted.");
|
||||||
//!
|
//!
|
||||||
//! // == 4. Verify Final State ==
|
//! // // == 4. Verify Final State ==
|
||||||
//! let mut final_catalog = Catalog::new(db_path);
|
//! // let mut final_catalog = Catalog::new(db_path);
|
||||||
//! final_catalog.load_by_class::<Product>()?;
|
//! // final_catalog.load_by_class::<Product>()?;
|
||||||
//!
|
//!
|
||||||
//! let final_count = final_catalog.list_by_class::<Product>().count();
|
//! // let final_count = final_catalog.list_by_class::<Product>().count();
|
||||||
//! println!("✅ Final product count: {}", final_count);
|
//! // println!("✅ Final product count: {}", final_count);
|
||||||
//! assert_eq!(final_count, 2);
|
//! // assert_eq!(final_count, 2);
|
||||||
//!
|
//!
|
||||||
//! let updated_laptop: Product = final_catalog.get("p1")?.unwrap();
|
//! // let updated_laptop: Product = final_catalog.get("p1")?.unwrap();
|
||||||
//! println!("✅ Verified updated laptop price: {}", updated_laptop.price);
|
//! // println!("✅ Verified updated laptop price: {}", updated_laptop.price);
|
||||||
//! assert_eq!(updated_laptop.price, 1150);
|
//! // assert_eq!(updated_laptop.price, 1150);
|
||||||
//!
|
//!
|
||||||
//! let deleted_keyboard: Option<Product> = final_catalog.get("p3")?;
|
//! // let deleted_keyboard: Option<Product> = final_catalog.get("p3")?;
|
||||||
//! println!("✅ Verified keyboard is deleted.");
|
//! // println!("✅ Verified keyboard is deleted.");
|
||||||
//! assert!(deleted_keyboard.is_none());
|
//! // assert!(deleted_keyboard.is_none());
|
||||||
//!
|
//!
|
||||||
//! // Clean up the created database file
|
//! // // Clean up the created database file
|
||||||
//! std::fs::remove_file(db_path).unwrap();
|
//! // std::fs::remove_file(db_path).unwrap();
|
||||||
//!
|
//!
|
||||||
//! Ok(())
|
//! // Ok(())
|
||||||
//! }
|
//! // }
|
||||||
//! ```
|
//! ```
|
||||||
use std::*;
|
use std::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,49 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::*;
|
||||||
|
|
||||||
/// Represents a catalog of entities that can be persisted to a SQLite database.
|
/// Represents a catalog of entities that can be persisted to a SQLite database.
|
||||||
///
|
///
|
||||||
/// The `Catalog` holds entities in memory and provides methods to interact with
|
/// The `Catalog` holds entities in memory and provides methods to interact with
|
||||||
/// them, as well as to persist changes to and load data from a database file.
|
/// them, as well as to persist changes to and load data from a database file.
|
||||||
#[derive(Debug, Default, PartialEq, Clone)]
|
#[derive(Debug, Default)]
|
||||||
pub struct O {
|
pub struct O {
|
||||||
pub(crate) path: String,
|
pub(crate) path: String,
|
||||||
pub(crate) items: HashMap<String, Entity>,
|
items: Mutex<HashMap<String, Entity>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Catalog {
|
||||||
|
/// Creates a new, empty in-memory `Catalog` for the database at the given path.
|
||||||
|
///
|
||||||
|
/// This method does not create the database file or connect to it. It only
|
||||||
|
/// initializes an empty catalog in memory. The database file will be accessed
|
||||||
|
/// when `init()`, `persist()`, or `load_*` methods are called.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `path` - The path to the SQLite database file that this catalog will manage.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A new `Catalog` instance with an empty in-memory item cache.
|
||||||
|
pub fn new(path: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
path: String::from(path),
|
||||||
|
..Catalog::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn on_items<F>(&self, exec: F) -> Result<(), FailedTo>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut HashMap<String, Entity>) -> Result<(), FailedTo>,
|
||||||
|
{
|
||||||
|
let mut guarded_items = self.items.lock().map_err(|_| FailedTo::LockCatalog)?;
|
||||||
|
exec(&mut guarded_items)
|
||||||
|
}
|
||||||
|
pub(crate) fn with_items<F, R>(&self, exec: F) -> Result<R, FailedTo>
|
||||||
|
where
|
||||||
|
F: FnOnce(&HashMap<String, Entity>) -> Result<R, FailedTo>,
|
||||||
|
{
|
||||||
|
let mut guarded_items = self.items.lock().map_err(|_| FailedTo::LockCatalog)?;
|
||||||
|
exec(&mut guarded_items)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ pub enum FailedTo {
|
|||||||
InitDatabase,
|
InitDatabase,
|
||||||
/// Failed to load data from the database.
|
/// Failed to load data from the database.
|
||||||
LoadFromDB,
|
LoadFromDB,
|
||||||
|
/// Failed to lock catalog in a multithread environment.
|
||||||
|
LockCatalog,
|
||||||
/// Failed to map a database row to an attribute.
|
/// Failed to map a database row to an attribute.
|
||||||
MapAttribute,
|
MapAttribute,
|
||||||
/// Failed to map a database row to an entity.
|
/// Failed to map a database row to an entity.
|
||||||
|
|||||||
@@ -16,8 +16,13 @@ mod tests {
|
|||||||
let item_id = item.id.clone();
|
let item_id = item.id.clone();
|
||||||
let _ = catalog.upsert(item);
|
let _ = catalog.upsert(item);
|
||||||
catalog.delete(&item_id);
|
catalog.delete(&item_id);
|
||||||
let entity = catalog.items.get(&item_id).unwrap();
|
let is_deleted = catalog
|
||||||
assert_eq!(entity.state, EntityState::ToDelete);
|
.with_items(|items| {
|
||||||
|
let entity = items.get(&item_id).unwrap();
|
||||||
|
Ok(entity.state == EntityState::ToDelete)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
assert!(is_deleted);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn delete_should_have_no_effect_for_nonexistent_id() {
|
fn delete_should_have_no_effect_for_nonexistent_id() {
|
||||||
@@ -32,9 +37,14 @@ mod tests {
|
|||||||
..Item::default()
|
..Item::default()
|
||||||
};
|
};
|
||||||
let _ = catalog.upsert(item);
|
let _ = catalog.upsert(item);
|
||||||
let original_items = catalog.items.clone();
|
|
||||||
// Attempt to delete a non-existent entity, which should not panic or change anything.
|
// Attempt to delete a non-existent entity, which should not panic or change anything.
|
||||||
catalog.delete("nonexistent-id");
|
catalog.delete("nonexistent-id");
|
||||||
assert_eq!(catalog.items, original_items);
|
let not_deleted = catalog
|
||||||
|
.with_items(|items| {
|
||||||
|
let entity = items.get("item-123").unwrap();
|
||||||
|
Ok(entity.state != EntityState::ToDelete)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
assert!(not_deleted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,24 @@ mod tests {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
let _ = catalog.insert_many(items);
|
let _ = catalog.insert_many(items);
|
||||||
assert_eq!(catalog.items.len(), 2);
|
let len = catalog.len().unwrap();
|
||||||
let entity1 = catalog.items.get("item-1").unwrap();
|
assert_eq!(len, 2);
|
||||||
|
let entity1 = catalog
|
||||||
|
.with_items(|items| {
|
||||||
|
let entity = items.get("item-1").unwrap();
|
||||||
|
Ok(entity.clone())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert_eq!(entity1.state, EntityState::New);
|
assert_eq!(entity1.state, EntityState::New);
|
||||||
assert_eq!(entity1.value_of("name"), Some(&Value::from("Item 1")));
|
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("price"), Some(&Value::from(10u64)));
|
||||||
assert_eq!(entity1.value_of("sell_trend"), Some(&Value::from(0i64)));
|
assert_eq!(entity1.value_of("sell_trend"), Some(&Value::from(0i64)));
|
||||||
let entity2 = catalog.items.get("item-2").unwrap();
|
let entity2 = catalog
|
||||||
|
.with_items(|items| {
|
||||||
|
let entity = items.get("item-2").unwrap();
|
||||||
|
Ok(entity.clone())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
assert_eq!(entity2.state, EntityState::New);
|
assert_eq!(entity2.state, EntityState::New);
|
||||||
assert_eq!(entity2.value_of("name"), Some(&Value::from("Item 2")));
|
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("price"), Some(&Value::from(20u64)));
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ mod tests {
|
|||||||
catalog2.load_by_class::<Item>().unwrap();
|
catalog2.load_by_class::<Item>().unwrap();
|
||||||
let mut loaded_items: Vec<Item> = catalog2
|
let mut loaded_items: Vec<Item> = catalog2
|
||||||
.list_by_class::<Item>()
|
.list_by_class::<Item>()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
.map(|item| item.unwrap())
|
.map(|item| item.unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
// Sort by ID to ensure consistent order for comparison
|
// Sort by ID to ensure consistent order for comparison
|
||||||
|
|||||||
@@ -25,22 +25,16 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let _ = catalog.upsert(item1.clone());
|
let _ = catalog.upsert(item1.clone());
|
||||||
let _ = catalog.upsert(item2.clone());
|
let _ = catalog.upsert(item2.clone());
|
||||||
let results: Vec<Item> = catalog
|
let results = catalog.list_by_class::<Item>().unwrap();
|
||||||
.list_by_class::<Item>()
|
|
||||||
.map(|item| item.unwrap())
|
|
||||||
.collect();
|
|
||||||
assert_eq!(results.len(), 2);
|
assert_eq!(results.len(), 2);
|
||||||
assert!(results.contains(&item1));
|
assert!(results.contains(&Ok(item1)));
|
||||||
assert!(results.contains(&item2));
|
assert!(results.contains(&Ok(item2)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn list_by_class_should_return_empty_iterator_if_no_match() {
|
fn list_by_class_should_return_empty_iterator_if_no_match() {
|
||||||
// Should return an empty iterator if no entities of that class exist.
|
// Should return an empty iterator if no entities of that class exist.
|
||||||
let catalog = Catalog::new("dummy.db");
|
let catalog = Catalog::new("dummy.db");
|
||||||
let results: Vec<Item> = catalog
|
let results: Vec<_> = catalog.list_by_class::<Item>().unwrap();
|
||||||
.list_by_class::<Item>()
|
|
||||||
.map(|item| item.unwrap())
|
|
||||||
.collect();
|
|
||||||
assert!(results.is_empty());
|
assert!(results.is_empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ mod tests {
|
|||||||
let _ = catalog.upsert(item1.clone());
|
let _ = catalog.upsert(item1.clone());
|
||||||
let _ = catalog.upsert(item2.clone());
|
let _ = catalog.upsert(item2.clone());
|
||||||
let _ = catalog.upsert(item3.clone());
|
let _ = catalog.upsert(item3.clone());
|
||||||
let results: Vec<Item> = catalog
|
let results: Vec<_> = catalog
|
||||||
.list_by_class_and_subclass("electronics")
|
.list_by_class_and_subclass::<Item>("electronics")
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
.map(|item| item.unwrap())
|
.map(|item| item.unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(results.len(), 2);
|
assert_eq!(results.len(), 2);
|
||||||
@@ -40,8 +42,10 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let _ = catalog.upsert(item1.clone());
|
let _ = catalog.upsert(item1.clone());
|
||||||
let results: Vec<Item> = catalog
|
let results: Vec<_> = catalog
|
||||||
.list_by_class_and_subclass("books")
|
.list_by_class_and_subclass::<Item>("books")
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
.map(|item| item.unwrap())
|
.map(|item| item.unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
assert!(results.is_empty());
|
assert!(results.is_empty());
|
||||||
|
|||||||
@@ -39,17 +39,22 @@ mod tests {
|
|||||||
let result = catalog2.load_by_class::<Item>();
|
let result = catalog2.load_by_class::<Item>();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
// 3. Verify that all items of that class were loaded
|
// 3. Verify that all items of that class were loaded
|
||||||
assert_eq!(catalog2.items.len(), 2);
|
let len = catalog2.len().unwrap();
|
||||||
|
assert_eq!(len, 2);
|
||||||
let loaded_item1: Item = catalog2.get("item-1").unwrap().unwrap();
|
let loaded_item1: Item = catalog2.get("item-1").unwrap().unwrap();
|
||||||
let loaded_item2: Item = catalog2.get("item-2").unwrap().unwrap();
|
let loaded_item2: Item = catalog2.get("item-2").unwrap().unwrap();
|
||||||
assert_eq!(loaded_item1, item1);
|
assert_eq!(loaded_item1, item1);
|
||||||
assert_eq!(loaded_item2, item2);
|
assert_eq!(loaded_item2, item2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog2.items.get("item-1").unwrap().state,
|
catalog2
|
||||||
|
.with_items(|items| { Ok(items.get("item-1").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Loaded
|
EntityState::Loaded
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog2.items.get("item-2").unwrap().state,
|
catalog2
|
||||||
|
.with_items(|items| { Ok(items.get("item-2").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Loaded
|
EntityState::Loaded
|
||||||
);
|
);
|
||||||
// Clean up
|
// Clean up
|
||||||
@@ -90,7 +95,8 @@ mod tests {
|
|||||||
..Item::default()
|
..Item::default()
|
||||||
};
|
};
|
||||||
let _ = catalog2.upsert(item_in_memory);
|
let _ = catalog2.upsert(item_in_memory);
|
||||||
assert_eq!(catalog2.items.len(), 1);
|
let len = catalog2.len().unwrap();
|
||||||
|
assert_eq!(len, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog2.get::<Item>("item-1").unwrap().unwrap().name,
|
catalog2.get::<Item>("item-1").unwrap().unwrap().name,
|
||||||
"Memory Version"
|
"Memory Version"
|
||||||
@@ -99,11 +105,14 @@ mod tests {
|
|||||||
let result = catalog2.load_by_class::<Item>();
|
let result = catalog2.load_by_class::<Item>();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
// 4. Verify that the in-memory entity has been replaced with the one from the DB.
|
// 4. Verify that the in-memory entity has been replaced with the one from the DB.
|
||||||
assert_eq!(catalog2.items.len(), 1);
|
let len = catalog2.len().unwrap();
|
||||||
|
assert_eq!(len, 1);
|
||||||
let loaded_item: Item = catalog2.get("item-1").unwrap().unwrap();
|
let loaded_item: Item = catalog2.get("item-1").unwrap().unwrap();
|
||||||
assert_eq!(loaded_item, item_in_db);
|
assert_eq!(loaded_item, item_in_db);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog2.items.get("item-1").unwrap().state,
|
catalog2
|
||||||
|
.with_items(|items| { Ok(items.get("item-1").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Loaded
|
EntityState::Loaded
|
||||||
);
|
);
|
||||||
// Clean up
|
// Clean up
|
||||||
@@ -124,7 +133,8 @@ mod tests {
|
|||||||
// 2. Attempt to load from the empty DB.
|
// 2. Attempt to load from the empty DB.
|
||||||
let result = catalog.load_by_class::<Item>();
|
let result = catalog.load_by_class::<Item>();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(catalog.items.is_empty());
|
let is_empty = catalog.is_empty().unwrap();
|
||||||
|
assert!(is_empty);
|
||||||
// 3. Add an item to memory and try loading again from the empty DB.
|
// 3. Add an item to memory and try loading again from the empty DB.
|
||||||
let item_in_memory = Item {
|
let item_in_memory = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
@@ -136,11 +146,13 @@ mod tests {
|
|||||||
..Item::default()
|
..Item::default()
|
||||||
};
|
};
|
||||||
let _ = catalog.upsert(item_in_memory.clone());
|
let _ = catalog.upsert(item_in_memory.clone());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
let len = catalog.len().unwrap();
|
||||||
|
assert_eq!(len, 1);
|
||||||
let result2 = catalog.load_by_class::<Item>();
|
let result2 = catalog.load_by_class::<Item>();
|
||||||
assert!(result2.is_ok());
|
assert!(result2.is_ok());
|
||||||
// 4. Verify the in-memory item is untouched because nothing was loaded from DB.
|
// 4. Verify the in-memory item is untouched because nothing was loaded from DB.
|
||||||
assert_eq!(catalog.items.len(), 1);
|
let len = catalog.len().unwrap();
|
||||||
|
assert_eq!(len, 1);
|
||||||
let retrieved_item: Item = catalog.get("item-1").unwrap().unwrap();
|
let retrieved_item: Item = catalog.get("item-1").unwrap().unwrap();
|
||||||
assert_eq!(retrieved_item, item_in_memory);
|
assert_eq!(retrieved_item, item_in_memory);
|
||||||
// Clean up
|
// Clean up
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_bool("in_stock", true);
|
let filter = Filter::new().with_bool("in_stock", true);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -82,9 +82,9 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_bool("in_stock", false);
|
let filter = Filter::new().with_bool("in_stock", false);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-1"));
|
assert!(!catalog.contains_key("item-1").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -121,7 +121,7 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new();
|
let filter = Filter::new();
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -216,10 +216,10 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Equal, 10);
|
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Equal, 10);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-2"));
|
assert!(!catalog.contains_key("item-2").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -263,10 +263,10 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Equal, -5);
|
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Equal, -5);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-1"));
|
assert!(!catalog.contains_key("item-1").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-3"));
|
assert!(!catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -310,10 +310,10 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Greater, 5);
|
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Greater, 5);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-2"));
|
assert!(!catalog.contains_key("item-2").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -358,21 +358,21 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_signed_int("sell_trend", Comparison::Greater, 10);
|
let filter1 = Filter::new().with_signed_int("sell_trend", Comparison::Greater, 10);
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 1);
|
assert_eq!(catalog1.len().unwrap(), 1);
|
||||||
assert!(catalog1.items.contains_key("item-3"));
|
assert!(catalog1.contains_key("item-3").unwrap());
|
||||||
// Edge Case 2: No values greater than filter value.
|
// Edge Case 2: No values greater than filter value.
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_signed_int("sell_trend", Comparison::Greater, 20);
|
let filter2 = Filter::new().with_signed_int("sell_trend", Comparison::Greater, 20);
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
// Edge Case 3: All values greater than filter value.
|
// Edge Case 3: All values greater than filter value.
|
||||||
let mut catalog3 = Catalog::new(db_path);
|
let mut catalog3 = Catalog::new(db_path);
|
||||||
let filter3 = Filter::new().with_signed_int("sell_trend", Comparison::Greater, -10);
|
let filter3 = Filter::new().with_signed_int("sell_trend", Comparison::Greater, -10);
|
||||||
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
||||||
assert_eq!(catalog3.items.len(), 3);
|
assert_eq!(catalog3.len().unwrap(), 3);
|
||||||
assert!(catalog3.items.contains_key("item-1"));
|
assert!(catalog3.contains_key("item-1").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-2"));
|
assert!(catalog3.contains_key("item-2").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-3"));
|
assert!(catalog3.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -416,10 +416,10 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Lesser, 15);
|
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Lesser, 15);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-3"));
|
assert!(!catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -464,21 +464,21 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_signed_int("sell_trend", Comparison::Lesser, 10);
|
let filter1 = Filter::new().with_signed_int("sell_trend", Comparison::Lesser, 10);
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 1);
|
assert_eq!(catalog1.len().unwrap(), 1);
|
||||||
assert!(catalog1.items.contains_key("item-2"));
|
assert!(catalog1.contains_key("item-2").unwrap());
|
||||||
// Edge Case 2: No values lesser than filter value.
|
// Edge Case 2: No values lesser than filter value.
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_signed_int("sell_trend", Comparison::Lesser, -5);
|
let filter2 = Filter::new().with_signed_int("sell_trend", Comparison::Lesser, -5);
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
// Edge Case 3: All values lesser than filter value.
|
// Edge Case 3: All values lesser than filter value.
|
||||||
let mut catalog3 = Catalog::new(db_path);
|
let mut catalog3 = Catalog::new(db_path);
|
||||||
let filter3 = Filter::new().with_signed_int("sell_trend", Comparison::Lesser, 25);
|
let filter3 = Filter::new().with_signed_int("sell_trend", Comparison::Lesser, 25);
|
||||||
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
||||||
assert_eq!(catalog3.items.len(), 3);
|
assert_eq!(catalog3.len().unwrap(), 3);
|
||||||
assert!(catalog3.items.contains_key("item-1"));
|
assert!(catalog3.contains_key("item-1").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-2"));
|
assert!(catalog3.contains_key("item-2").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-3"));
|
assert!(catalog3.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -522,10 +522,10 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_signed_int("sell_trend", Comparison::GreaterOrEqual, 10);
|
let filter = Filter::new().with_signed_int("sell_trend", Comparison::GreaterOrEqual, 10);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-2"));
|
assert!(!catalog.contains_key("item-2").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -570,21 +570,21 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_signed_int("sell_trend", Comparison::GreaterOrEqual, 20);
|
let filter1 = Filter::new().with_signed_int("sell_trend", Comparison::GreaterOrEqual, 20);
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 1);
|
assert_eq!(catalog1.len().unwrap(), 1);
|
||||||
assert!(catalog1.items.contains_key("item-3"));
|
assert!(catalog1.contains_key("item-3").unwrap());
|
||||||
// Edge Case 2: No values greater than or equal to filter value.
|
// Edge Case 2: No values greater than or equal to filter value.
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_signed_int("sell_trend", Comparison::GreaterOrEqual, 21);
|
let filter2 = Filter::new().with_signed_int("sell_trend", Comparison::GreaterOrEqual, 21);
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
// Edge Case 3: All values greater than or equal to filter value.
|
// Edge Case 3: All values greater than or equal to filter value.
|
||||||
let mut catalog3 = Catalog::new(db_path);
|
let mut catalog3 = Catalog::new(db_path);
|
||||||
let filter3 = Filter::new().with_signed_int("sell_trend", Comparison::GreaterOrEqual, -5);
|
let filter3 = Filter::new().with_signed_int("sell_trend", Comparison::GreaterOrEqual, -5);
|
||||||
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
||||||
assert_eq!(catalog3.items.len(), 3);
|
assert_eq!(catalog3.len().unwrap(), 3);
|
||||||
assert!(catalog3.items.contains_key("item-1"));
|
assert!(catalog3.contains_key("item-1").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-2"));
|
assert!(catalog3.contains_key("item-2").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-3"));
|
assert!(catalog3.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -628,10 +628,10 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_signed_int("sell_trend", Comparison::LesserOrEqual, 10);
|
let filter = Filter::new().with_signed_int("sell_trend", Comparison::LesserOrEqual, 10);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-3"));
|
assert!(!catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -676,21 +676,21 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_signed_int("sell_trend", Comparison::LesserOrEqual, -5);
|
let filter1 = Filter::new().with_signed_int("sell_trend", Comparison::LesserOrEqual, -5);
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 1);
|
assert_eq!(catalog1.len().unwrap(), 1);
|
||||||
assert!(catalog1.items.contains_key("item-2"));
|
assert!(catalog1.contains_key("item-2").unwrap());
|
||||||
// Edge Case 2: No values lesser than or equal to filter value.
|
// Edge Case 2: No values lesser than or equal to filter value.
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_signed_int("sell_trend", Comparison::LesserOrEqual, -6);
|
let filter2 = Filter::new().with_signed_int("sell_trend", Comparison::LesserOrEqual, -6);
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
// Edge Case 3: All values lesser than or equal to filter value.
|
// Edge Case 3: All values lesser than or equal to filter value.
|
||||||
let mut catalog3 = Catalog::new(db_path);
|
let mut catalog3 = Catalog::new(db_path);
|
||||||
let filter3 = Filter::new().with_signed_int("sell_trend", Comparison::LesserOrEqual, 20);
|
let filter3 = Filter::new().with_signed_int("sell_trend", Comparison::LesserOrEqual, 20);
|
||||||
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
||||||
assert_eq!(catalog3.items.len(), 3);
|
assert_eq!(catalog3.len().unwrap(), 3);
|
||||||
assert!(catalog3.items.contains_key("item-1"));
|
assert!(catalog3.contains_key("item-1").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-2"));
|
assert!(catalog3.contains_key("item-2").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-3"));
|
assert!(catalog3.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -735,44 +735,44 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_unsigned_int("price", Comparison::Equal, 200);
|
let filter = Filter::new().with_unsigned_int("price", Comparison::Equal, 200);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
// Greater
|
// Greater
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_unsigned_int("price", Comparison::Greater, 200);
|
let filter = Filter::new().with_unsigned_int("price", Comparison::Greater, 200);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
// Lesser
|
// Lesser
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_unsigned_int("price", Comparison::Lesser, 200);
|
let filter = Filter::new().with_unsigned_int("price", Comparison::Lesser, 200);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
// GreaterOrEqual
|
// GreaterOrEqual
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_unsigned_int("price", Comparison::GreaterOrEqual, 200);
|
let filter = Filter::new().with_unsigned_int("price", Comparison::GreaterOrEqual, 200);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
// LesserOrEqual
|
// LesserOrEqual
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_unsigned_int("price", Comparison::LesserOrEqual, 200);
|
let filter = Filter::new().with_unsigned_int("price", Comparison::LesserOrEqual, 200);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
// Edge case: No match
|
// Edge case: No match
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_unsigned_int("price", Comparison::Equal, 400);
|
let filter = Filter::new().with_unsigned_int("price", Comparison::Equal, 400);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert!(catalog.items.is_empty());
|
assert!(catalog.is_empty().unwrap());
|
||||||
// Edge case: All match
|
// Edge case: All match
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_unsigned_int("price", Comparison::GreaterOrEqual, 100);
|
let filter = Filter::new().with_unsigned_int("price", Comparison::GreaterOrEqual, 100);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 3);
|
assert_eq!(catalog.len().unwrap(), 3);
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -817,24 +817,24 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::IsExactly, "Item One");
|
let filter = Filter::new().with_text("name", Comparison::IsExactly, "Item One");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
// Partial match should not work
|
// Partial match should not work
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::IsExactly, "Item");
|
let filter = Filter::new().with_text("name", Comparison::IsExactly, "Item");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert!(catalog.items.is_empty());
|
assert!(catalog.is_empty().unwrap());
|
||||||
// Case insensitive match
|
// Case insensitive match
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::IsExactly, "item one");
|
let filter = Filter::new().with_text("name", Comparison::IsExactly, "item one");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
// No match
|
// No match
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::IsExactly, "Item Four");
|
let filter = Filter::new().with_text("name", Comparison::IsExactly, "Item Four");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert!(catalog.items.is_empty());
|
assert!(catalog.is_empty().unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -879,34 +879,34 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::StartsWith, "Item");
|
let filter = Filter::new().with_text("name", Comparison::StartsWith, "Item");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
// Starts with "I"
|
// Starts with "I"
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::StartsWith, "I");
|
let filter = Filter::new().with_text("name", Comparison::StartsWith, "I");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
// No match
|
// No match
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::StartsWith, "Z");
|
let filter = Filter::new().with_text("name", Comparison::StartsWith, "Z");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert!(catalog.items.is_empty());
|
assert!(catalog.is_empty().unwrap());
|
||||||
// Case insensitive
|
// Case insensitive
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::StartsWith, "item");
|
let filter = Filter::new().with_text("name", Comparison::StartsWith, "item");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
// Full string match
|
// Full string match
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::StartsWith, "Item One");
|
let filter = Filter::new().with_text("name", Comparison::StartsWith, "Item One");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -951,25 +951,25 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::EndsWith, "one");
|
let filter = Filter::new().with_text("name", Comparison::EndsWith, "one");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
// Ends with "item" - case insensitive
|
// Ends with "item" - case insensitive
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::EndsWith, "item");
|
let filter = Filter::new().with_text("name", Comparison::EndsWith, "item");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
// No match
|
// No match
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::EndsWith, "Z");
|
let filter = Filter::new().with_text("name", Comparison::EndsWith, "Z");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert!(catalog.items.is_empty());
|
assert!(catalog.is_empty().unwrap());
|
||||||
// Full string match - case insensitive
|
// Full string match - case insensitive
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::EndsWith, "item one");
|
let filter = Filter::new().with_text("name", Comparison::EndsWith, "item one");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1014,24 +1014,24 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::Contains, "item");
|
let filter = Filter::new().with_text("name", Comparison::Contains, "item");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 3);
|
assert_eq!(catalog.len().unwrap(), 3);
|
||||||
// Contains "THE" - case insensitive
|
// Contains "THE" - case insensitive
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::Contains, "THE");
|
let filter = Filter::new().with_text("name", Comparison::Contains, "THE");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
// No match
|
// No match
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::Contains, "Z");
|
let filter = Filter::new().with_text("name", Comparison::Contains, "Z");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert!(catalog.items.is_empty());
|
assert!(catalog.is_empty().unwrap());
|
||||||
// Full string match - case insensitive
|
// Full string match - case insensitive
|
||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_text("name", Comparison::Contains, "item one");
|
let filter = Filter::new().with_text("name", Comparison::Contains, "item one");
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1066,10 +1066,10 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_real("discount", Comparison::Equal, 0.10);
|
let filter = Filter::new().with_real("discount", Comparison::Equal, 0.10);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
assert!(!catalog.items.contains_key("item-2"));
|
assert!(!catalog.contains_key("item-2").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1104,9 +1104,9 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_real("discount", Comparison::Greater, 0.15);
|
let filter = Filter::new().with_real("discount", Comparison::Greater, 0.15);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1136,12 +1136,12 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_real("discount", Comparison::Greater, 0.10);
|
let filter1 = Filter::new().with_real("discount", Comparison::Greater, 0.10);
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 1);
|
assert_eq!(catalog1.len().unwrap(), 1);
|
||||||
assert!(catalog1.items.contains_key("item-2"));
|
assert!(catalog1.contains_key("item-2").unwrap());
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_real("discount", Comparison::Greater, 0.20);
|
let filter2 = Filter::new().with_real("discount", Comparison::Greater, 0.20);
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1176,9 +1176,9 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_real("discount", Comparison::Lesser, 0.15);
|
let filter = Filter::new().with_real("discount", Comparison::Lesser, 0.15);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1208,12 +1208,12 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_real("discount", Comparison::Lesser, 0.20);
|
let filter1 = Filter::new().with_real("discount", Comparison::Lesser, 0.20);
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 1);
|
assert_eq!(catalog1.len().unwrap(), 1);
|
||||||
assert!(catalog1.items.contains_key("item-1"));
|
assert!(catalog1.contains_key("item-1").unwrap());
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_real("discount", Comparison::Lesser, 0.10);
|
let filter2 = Filter::new().with_real("discount", Comparison::Lesser, 0.10);
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1248,9 +1248,9 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_real("discount", Comparison::GreaterOrEqual, 0.15);
|
let filter = Filter::new().with_real("discount", Comparison::GreaterOrEqual, 0.15);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-2"));
|
assert!(catalog.contains_key("item-2").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1280,12 +1280,12 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_real("discount", Comparison::GreaterOrEqual, 0.20);
|
let filter1 = Filter::new().with_real("discount", Comparison::GreaterOrEqual, 0.20);
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 1);
|
assert_eq!(catalog1.len().unwrap(), 1);
|
||||||
assert!(catalog1.items.contains_key("item-2"));
|
assert!(catalog1.contains_key("item-2").unwrap());
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_real("discount", Comparison::GreaterOrEqual, 0.21);
|
let filter2 = Filter::new().with_real("discount", Comparison::GreaterOrEqual, 0.21);
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1320,9 +1320,9 @@ mod tests {
|
|||||||
let mut catalog = Catalog::new(db_path);
|
let mut catalog = Catalog::new(db_path);
|
||||||
let filter = Filter::new().with_real("discount", Comparison::LesserOrEqual, 0.15);
|
let filter = Filter::new().with_real("discount", Comparison::LesserOrEqual, 0.15);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 2);
|
assert_eq!(catalog.len().unwrap(), 2);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
assert!(catalog.items.contains_key("item-3"));
|
assert!(catalog.contains_key("item-3").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1352,12 +1352,12 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_real("discount", Comparison::LesserOrEqual, 0.10);
|
let filter1 = Filter::new().with_real("discount", Comparison::LesserOrEqual, 0.10);
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 1);
|
assert_eq!(catalog1.len().unwrap(), 1);
|
||||||
assert!(catalog1.items.contains_key("item-1"));
|
assert!(catalog1.contains_key("item-1").unwrap());
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_real("discount", Comparison::LesserOrEqual, 0.09);
|
let filter2 = Filter::new().with_real("discount", Comparison::LesserOrEqual, 0.09);
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1408,8 +1408,8 @@ mod tests {
|
|||||||
.with_real("discount", Comparison::Equal, 0.10)
|
.with_real("discount", Comparison::Equal, 0.10)
|
||||||
.with_bool("in_stock", true);
|
.with_bool("in_stock", true);
|
||||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
assert!(catalog.items.contains_key("item-1"));
|
assert!(catalog.contains_key("item-1").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1478,30 +1478,30 @@ mod tests {
|
|||||||
let mut catalog1 = Catalog::new(db_path);
|
let mut catalog1 = Catalog::new(db_path);
|
||||||
let filter1 = Filter::new().with_class("item");
|
let filter1 = Filter::new().with_class("item");
|
||||||
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
assert!(catalog1.load_by_filter(&filter1).is_ok());
|
||||||
assert_eq!(catalog1.items.len(), 4);
|
assert_eq!(catalog1.len().unwrap(), 4);
|
||||||
assert!(catalog1.items.contains_key("item-1"));
|
assert!(catalog1.contains_key("item-1").unwrap());
|
||||||
assert!(catalog1.items.contains_key("item-2"));
|
assert!(catalog1.contains_key("item-2").unwrap());
|
||||||
assert!(catalog1.items.contains_key("item-3"));
|
assert!(catalog1.contains_key("item-3").unwrap());
|
||||||
assert!(catalog1.items.contains_key("item-4"));
|
assert!(catalog1.contains_key("item-4").unwrap());
|
||||||
// Test 2: Filter by class "another_item"
|
// Test 2: Filter by class "another_item"
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
let filter2 = Filter::new().with_class("another_item");
|
let filter2 = Filter::new().with_class("another_item");
|
||||||
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
assert!(catalog2.load_by_filter(&filter2).is_ok());
|
||||||
assert_eq!(catalog2.items.len(), 1);
|
assert_eq!(catalog2.len().unwrap(), 1);
|
||||||
assert!(catalog2.items.contains_key("another-1"));
|
assert!(catalog2.contains_key("another-1").unwrap());
|
||||||
// Test 3: Filter by class "item" and subclass "sub-a"
|
// Test 3: Filter by class "item" and subclass "sub-a"
|
||||||
let mut catalog3 = Catalog::new(db_path);
|
let mut catalog3 = Catalog::new(db_path);
|
||||||
let filter3 = Filter::new().with_class("item").with_subclass("sub-a");
|
let filter3 = Filter::new().with_class("item").with_subclass("sub-a");
|
||||||
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
assert!(catalog3.load_by_filter(&filter3).is_ok());
|
||||||
assert_eq!(catalog3.items.len(), 2);
|
assert_eq!(catalog3.len().unwrap(), 2);
|
||||||
assert!(catalog3.items.contains_key("item-1"));
|
assert!(catalog3.contains_key("item-1").unwrap());
|
||||||
assert!(catalog3.items.contains_key("item-3"));
|
assert!(catalog3.contains_key("item-3").unwrap());
|
||||||
// Test 4: Filter by class "item" and subclass "subitem" (the default)
|
// Test 4: Filter by class "item" and subclass "subitem" (the default)
|
||||||
let mut catalog4 = Catalog::new(db_path);
|
let mut catalog4 = Catalog::new(db_path);
|
||||||
let filter4 = Filter::new().with_class("item").with_subclass("subitem");
|
let filter4 = Filter::new().with_class("item").with_subclass("subitem");
|
||||||
assert!(catalog4.load_by_filter(&filter4).is_ok());
|
assert!(catalog4.load_by_filter(&filter4).is_ok());
|
||||||
assert_eq!(catalog4.items.len(), 1);
|
assert_eq!(catalog4.len().unwrap(), 1);
|
||||||
assert!(catalog4.items.contains_key("item-4"));
|
assert!(catalog4.contains_key("item-4").unwrap());
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,16 @@ mod tests {
|
|||||||
catalog1.persist().unwrap();
|
catalog1.persist().unwrap();
|
||||||
// 2. Create a new, empty catalog instance for the same DB.
|
// 2. Create a new, empty catalog instance for the same DB.
|
||||||
let mut catalog2 = Catalog::new(db_path);
|
let mut catalog2 = Catalog::new(db_path);
|
||||||
assert!(catalog2.items.is_empty());
|
assert!(catalog2.is_empty().unwrap());
|
||||||
// 3. Load the item by its ID.
|
// 3. Load the item by its ID.
|
||||||
let result = catalog2.load_by_id("item-1");
|
let result = catalog2.load_by_id("item-1");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
// 4. Verify the item is now in the in-memory 'items' map.
|
// 4. Verify the item is now in the in-memory 'items' map.
|
||||||
assert_eq!(catalog2.items.len(), 1);
|
let len = catalog2.len().unwrap();
|
||||||
let loaded_entity = catalog2.items.get("item-1").unwrap();
|
assert_eq!(len, 1);
|
||||||
|
let loaded_entity = catalog2
|
||||||
|
.with_items(|items| Ok(items.get("item-1").unwrap().clone()))
|
||||||
|
.unwrap();
|
||||||
// 5. Verify the loaded entity's data and state.
|
// 5. Verify the loaded entity's data and state.
|
||||||
assert_eq!(loaded_entity.id, "item-1");
|
assert_eq!(loaded_entity.id, "item-1");
|
||||||
assert_eq!(loaded_entity.class, "item");
|
assert_eq!(loaded_entity.class, "item");
|
||||||
@@ -88,7 +91,9 @@ mod tests {
|
|||||||
..Item::default()
|
..Item::default()
|
||||||
};
|
};
|
||||||
let _ = catalog2.upsert(item_in_memory);
|
let _ = catalog2.upsert(item_in_memory);
|
||||||
let entity_before_load = catalog2.items.get("item-1").unwrap();
|
let entity_before_load = catalog2
|
||||||
|
.with_items(|items| Ok(items.get("item-1").unwrap().clone()))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(entity_before_load.state, EntityState::New);
|
assert_eq!(entity_before_load.state, EntityState::New);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
entity_before_load.value_of("name"),
|
entity_before_load.value_of("name"),
|
||||||
@@ -98,7 +103,9 @@ mod tests {
|
|||||||
let result = catalog2.load_by_id("item-1");
|
let result = catalog2.load_by_id("item-1");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
// 4. Verify that the in-memory entity has been replaced with the one from the DB.
|
// 4. Verify that the in-memory entity has been replaced with the one from the DB.
|
||||||
let entity_after_load = catalog2.items.get("item-1").unwrap();
|
let entity_after_load = catalog2
|
||||||
|
.with_items(|items| Ok(items.get("item-1").unwrap().clone()))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(entity_after_load.state, EntityState::Loaded);
|
assert_eq!(entity_after_load.state, EntityState::Loaded);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
entity_after_load.value_of("name"),
|
entity_after_load.value_of("name"),
|
||||||
@@ -134,7 +141,7 @@ mod tests {
|
|||||||
let result = catalog.load_by_id("nonexistent-id");
|
let result = catalog.load_by_id("nonexistent-id");
|
||||||
// 3. Verify that the operation succeeded and the catalog remains empty.
|
// 3. Verify that the operation succeeded and the catalog remains empty.
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(catalog.items.is_empty());
|
assert!(catalog.is_empty().unwrap());
|
||||||
// Clean up
|
// Clean up
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ mod tests {
|
|||||||
let path = "test.db";
|
let path = "test.db";
|
||||||
let catalog = Catalog::new(path);
|
let catalog = Catalog::new(path);
|
||||||
assert_eq!(catalog.path, path);
|
assert_eq!(catalog.path, path);
|
||||||
assert!(catalog.items.is_empty());
|
assert!(catalog.is_empty().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let _ = catalog2.upsert(updated_item.clone());
|
let _ = catalog2.upsert(updated_item.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog2.items.get("item-1").unwrap().state,
|
catalog2
|
||||||
|
.with_items(|items| { Ok(items.get("item-1").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Updated
|
EntityState::Updated
|
||||||
);
|
);
|
||||||
// 4. Persist the changes.
|
// 4. Persist the changes.
|
||||||
@@ -197,7 +199,7 @@ mod tests {
|
|||||||
let mut catalog_verify = Catalog::new(db_path);
|
let mut catalog_verify = Catalog::new(db_path);
|
||||||
catalog_verify.load_by_class::<Item>().unwrap();
|
catalog_verify.load_by_class::<Item>().unwrap();
|
||||||
// Check total count
|
// Check total count
|
||||||
assert_eq!(catalog_verify.items.len(), 3);
|
assert_eq!(catalog_verify.len().unwrap(), 3);
|
||||||
// Verify added item
|
// Verify added item
|
||||||
let added_item: Item = catalog_verify.get("add-me").unwrap().unwrap();
|
let added_item: Item = catalog_verify.get("add-me").unwrap().unwrap();
|
||||||
assert_eq!(added_item, item_to_add);
|
assert_eq!(added_item, item_to_add);
|
||||||
@@ -277,17 +279,23 @@ mod tests {
|
|||||||
let _ = catalog.upsert(item_untouched.clone());
|
let _ = catalog.upsert(item_untouched.clone());
|
||||||
catalog.persist().unwrap();
|
catalog.persist().unwrap();
|
||||||
// At this point, all items are in the DB and in-memory state is `Loaded`.
|
// At this point, all items are in the DB and in-memory state is `Loaded`.
|
||||||
assert_eq!(catalog.items.len(), 3);
|
assert_eq!(catalog.len().unwrap(), 3);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog.items.get("update-me").unwrap().state,
|
catalog
|
||||||
|
.with_items(|items| { Ok(items.get("update-me").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Loaded
|
EntityState::Loaded
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog.items.get("delete-me").unwrap().state,
|
catalog
|
||||||
|
.with_items(|items| { Ok(items.get("delete-me").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Loaded
|
EntityState::Loaded
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog.items.get("keep-me").unwrap().state,
|
catalog
|
||||||
|
.with_items(|items| { Ok(items.get("keep-me").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Loaded
|
EntityState::Loaded
|
||||||
);
|
);
|
||||||
// 2. Manipulate the catalog to have entities in various states.
|
// 2. Manipulate the catalog to have entities in various states.
|
||||||
@@ -315,34 +323,49 @@ mod tests {
|
|||||||
catalog.delete("delete-me"); // State: ToDelete
|
catalog.delete("delete-me"); // State: ToDelete
|
||||||
// 'item_untouched' remains with state `Loaded`.
|
// 'item_untouched' remains with state `Loaded`.
|
||||||
// Check states before final persist
|
// Check states before final persist
|
||||||
assert_eq!(catalog.items.get("add-me").unwrap().state, EntityState::New);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog.items.get("update-me").unwrap().state,
|
catalog
|
||||||
|
.with_items(|items| { Ok(items.get("add-me").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
|
EntityState::New
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
catalog
|
||||||
|
.with_items(|items| { Ok(items.get("update-me").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Updated
|
EntityState::Updated
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog.items.get("delete-me").unwrap().state,
|
catalog
|
||||||
|
.with_items(|items| { Ok(items.get("delete-me").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::ToDelete
|
EntityState::ToDelete
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
catalog.items.get("keep-me").unwrap().state,
|
catalog
|
||||||
|
.with_items(|items| { Ok(items.get("keep-me").unwrap().state) })
|
||||||
|
.unwrap(),
|
||||||
EntityState::Loaded
|
EntityState::Loaded
|
||||||
);
|
);
|
||||||
assert_eq!(catalog.items.len(), 4);
|
assert_eq!(catalog.len().unwrap(), 4);
|
||||||
// 3. Persist all changes.
|
// 3. Persist all changes.
|
||||||
catalog.persist().unwrap();
|
catalog.persist().unwrap();
|
||||||
// 4. Verify the in-memory state after persisting.
|
// 4. Verify the in-memory state after persisting.
|
||||||
// The item marked for deletion should be gone.
|
// The item marked for deletion should be gone.
|
||||||
assert!(!catalog.items.contains_key("delete-me"));
|
assert!(!catalog.contains_key("delete-me").unwrap());
|
||||||
assert_eq!(catalog.items.len(), 3);
|
assert_eq!(catalog.len().unwrap(), 3);
|
||||||
// All remaining items should have their state as `Loaded`.
|
// All remaining items should have their state as `Loaded`.
|
||||||
let new_item_entity = catalog.items.get("add-me").unwrap();
|
let new_item_entity = catalog
|
||||||
|
.with_items(|items| Ok(items.get("add-me").unwrap().clone()))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(new_item_entity.state, EntityState::Loaded);
|
assert_eq!(new_item_entity.state, EntityState::Loaded);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
new_item_entity.value_of("name"),
|
new_item_entity.value_of("name"),
|
||||||
Some(&Value::from("Add Me"))
|
Some(&Value::from("Add Me"))
|
||||||
);
|
);
|
||||||
let updated_item_entity = catalog.items.get("update-me").unwrap();
|
let updated_item_entity = catalog
|
||||||
|
.with_items(|items| Ok(items.get("update-me").unwrap().clone()))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(updated_item_entity.state, EntityState::Loaded);
|
assert_eq!(updated_item_entity.state, EntityState::Loaded);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
updated_item_entity.value_of("name"),
|
updated_item_entity.value_of("name"),
|
||||||
@@ -352,7 +375,9 @@ mod tests {
|
|||||||
updated_item_entity.value_of("sell_trend"),
|
updated_item_entity.value_of("sell_trend"),
|
||||||
Some(&Value::from(10i64))
|
Some(&Value::from(10i64))
|
||||||
);
|
);
|
||||||
let untouched_item_entity = catalog.items.get("keep-me").unwrap();
|
let untouched_item_entity = catalog
|
||||||
|
.with_items(|items| Ok(items.get("keep-me").unwrap().clone()))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(untouched_item_entity.state, EntityState::Loaded);
|
assert_eq!(untouched_item_entity.state, EntityState::Loaded);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
untouched_item_entity.value_of("name"),
|
untouched_item_entity.value_of("name"),
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let item_id = item.id.clone();
|
let item_id = item.id.clone();
|
||||||
let _ = catalog.upsert(item);
|
let _ = catalog.upsert(item);
|
||||||
let entity = catalog.items.get(&item_id).unwrap();
|
let entity = catalog
|
||||||
|
.with_items(|items| Ok(items.get(&item_id).unwrap().clone()))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(entity.id, item_id);
|
assert_eq!(entity.id, item_id);
|
||||||
assert_eq!(entity.state, EntityState::New);
|
assert_eq!(entity.state, EntityState::New);
|
||||||
assert_eq!(entity.class, "item");
|
assert_eq!(entity.class, "item");
|
||||||
@@ -47,8 +49,10 @@ mod tests {
|
|||||||
..Item::default()
|
..Item::default()
|
||||||
};
|
};
|
||||||
let _ = catalog.upsert(item2);
|
let _ = catalog.upsert(item2);
|
||||||
assert_eq!(catalog.items.len(), 1);
|
assert_eq!(catalog.len().unwrap(), 1);
|
||||||
let entity = catalog.items.get(&item_id).unwrap();
|
let entity = catalog
|
||||||
|
.with_items(|items| Ok(items.get(&item_id).unwrap().clone()))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(entity.value_of("name"), Some(&Value::from("Second Item")));
|
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("price"), Some(&Value::from(200u64)));
|
||||||
assert_eq!(entity.value_of("sell_trend"), Some(&Value::from(10i64)));
|
assert_eq!(entity.value_of("sell_trend"), Some(&Value::from(10i64)));
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ mod tests {
|
|||||||
// Verifies that entities marked as 'New' in the catalog are inserted into the database, along with all their attributes.
|
// Verifies that entities marked as 'New' in the catalog are inserted into the database, along with all their attributes.
|
||||||
let db_path = Path::new("test_insert.db");
|
let db_path = Path::new("test_insert.db");
|
||||||
let conn = setup_db(db_path);
|
let conn = setup_db(db_path);
|
||||||
let mut catalog = Catalog::new(db_path.to_str().unwrap());
|
let catalog = Catalog::new(db_path.to_str().unwrap());
|
||||||
let mut entity = Entity {
|
let mut entity = Entity {
|
||||||
id: "e1".to_string(),
|
id: "e1".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
@@ -38,8 +38,14 @@ mod tests {
|
|||||||
value: Value::Text("v1".to_string()),
|
value: Value::Text("v1".to_string()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
catalog.items.insert("e1".to_string(), entity);
|
let _ = catalog.on_items(|items| {
|
||||||
assert!(sqlite::persist::catalog(db_path, &catalog).is_ok());
|
items.insert("e1".to_string(), entity);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
let is_ok = catalog
|
||||||
|
.with_items(|items| Ok(sqlite::persist::catalog(db_path, items).is_ok()))
|
||||||
|
.unwrap();
|
||||||
|
assert!(is_ok);
|
||||||
assert_eq!(count_rows(&conn, "entity", "id = 'e1'"), 1);
|
assert_eq!(count_rows(&conn, "entity", "id = 'e1'"), 1);
|
||||||
assert_eq!(count_rows(&conn, "attribute", "entity_id = 'e1'"), 1);
|
assert_eq!(count_rows(&conn, "attribute", "entity_id = 'e1'"), 1);
|
||||||
fs::remove_file(db_path).unwrap();
|
fs::remove_file(db_path).unwrap();
|
||||||
@@ -51,7 +57,7 @@ mod tests {
|
|||||||
let conn = setup_db(db_path);
|
let conn = setup_db(db_path);
|
||||||
conn.execute("INSERT INTO entity (id, class) VALUES ('e1', 'c1')", [])
|
conn.execute("INSERT INTO entity (id, class) VALUES ('e1', 'c1')", [])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut catalog = Catalog::new(db_path.to_str().unwrap());
|
let catalog = Catalog::new(db_path.to_str().unwrap());
|
||||||
let entity = Entity {
|
let entity = Entity {
|
||||||
id: "e1".to_string(),
|
id: "e1".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
@@ -60,8 +66,14 @@ mod tests {
|
|||||||
state: EntityState::ToDelete,
|
state: EntityState::ToDelete,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
};
|
};
|
||||||
catalog.items.insert("e1".to_string(), entity);
|
let _ = catalog.on_items(|items| {
|
||||||
assert!(sqlite::persist::catalog(db_path, &catalog).is_ok());
|
items.insert("e1".to_string(), entity);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
let is_ok = catalog
|
||||||
|
.with_items(|items| Ok(sqlite::persist::catalog(db_path, items).is_ok()))
|
||||||
|
.unwrap();
|
||||||
|
assert!(is_ok);
|
||||||
assert_eq!(count_rows(&conn, "entity", "id = 'e1'"), 0);
|
assert_eq!(count_rows(&conn, "entity", "id = 'e1'"), 0);
|
||||||
fs::remove_file(db_path).unwrap();
|
fs::remove_file(db_path).unwrap();
|
||||||
}
|
}
|
||||||
@@ -72,7 +84,7 @@ mod tests {
|
|||||||
let conn = setup_db(db_path);
|
let conn = setup_db(db_path);
|
||||||
conn.execute("INSERT INTO entity (id, class) VALUES ('e1', 'c1')", [])
|
conn.execute("INSERT INTO entity (id, class) VALUES ('e1', 'c1')", [])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut catalog = Catalog::new(db_path.to_str().unwrap());
|
let catalog = Catalog::new(db_path.to_str().unwrap());
|
||||||
let to_delete = Entity {
|
let to_delete = Entity {
|
||||||
id: "e1".to_string(),
|
id: "e1".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
@@ -89,9 +101,18 @@ mod tests {
|
|||||||
state: EntityState::New,
|
state: EntityState::New,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
};
|
};
|
||||||
catalog.items.insert("e1".to_string(), to_delete);
|
let _ = catalog.on_items(|items| {
|
||||||
catalog.items.insert("e2".to_string(), to_add);
|
items.insert("e1".to_string(), to_delete);
|
||||||
assert!(sqlite::persist::catalog(db_path, &catalog).is_ok());
|
Ok(())
|
||||||
|
});
|
||||||
|
let _ = catalog.on_items(|items| {
|
||||||
|
items.insert("e2".to_string(), to_add);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
let is_ok = catalog
|
||||||
|
.with_items(|items| Ok(sqlite::persist::catalog(db_path, items).is_ok()))
|
||||||
|
.unwrap();
|
||||||
|
assert!(is_ok);
|
||||||
assert_eq!(count_rows(&conn, "entity", "id = 'e1'"), 0);
|
assert_eq!(count_rows(&conn, "entity", "id = 'e1'"), 0);
|
||||||
assert_eq!(count_rows(&conn, "entity", "id = 'e2'"), 1);
|
assert_eq!(count_rows(&conn, "entity", "id = 'e2'"), 1);
|
||||||
fs::remove_file(db_path).unwrap();
|
fs::remove_file(db_path).unwrap();
|
||||||
@@ -103,7 +124,7 @@ mod tests {
|
|||||||
let conn = setup_db(db_path);
|
let conn = setup_db(db_path);
|
||||||
conn.execute("INSERT INTO entity (id, class) VALUES ('e1', 'c1')", [])
|
conn.execute("INSERT INTO entity (id, class) VALUES ('e1', 'c1')", [])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut catalog = Catalog::new(db_path.to_str().unwrap());
|
let catalog = Catalog::new(db_path.to_str().unwrap());
|
||||||
let unmodified = Entity {
|
let unmodified = Entity {
|
||||||
id: "e1".to_string(),
|
id: "e1".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
@@ -112,8 +133,14 @@ mod tests {
|
|||||||
state: EntityState::Loaded,
|
state: EntityState::Loaded,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
};
|
};
|
||||||
catalog.items.insert("e1".to_string(), unmodified);
|
let _ = catalog.on_items(|items| {
|
||||||
assert!(sqlite::persist::catalog(db_path, &catalog).is_ok());
|
items.insert("e1".to_string(), unmodified);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
let is_ok = catalog
|
||||||
|
.with_items(|items| Ok(sqlite::persist::catalog(db_path, items).is_ok()))
|
||||||
|
.unwrap();
|
||||||
|
assert!(is_ok);
|
||||||
assert_eq!(count_rows(&conn, "entity", "id = 'e1'"), 1);
|
assert_eq!(count_rows(&conn, "entity", "id = 'e1'"), 1);
|
||||||
fs::remove_file(db_path).unwrap();
|
fs::remove_file(db_path).unwrap();
|
||||||
}
|
}
|
||||||
@@ -127,7 +154,7 @@ mod tests {
|
|||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut catalog = Catalog::new(db_path.to_str().unwrap());
|
let catalog = Catalog::new(db_path.to_str().unwrap());
|
||||||
let mut new_entity = Entity {
|
let mut new_entity = Entity {
|
||||||
id: "e_new".to_string(),
|
id: "e_new".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
@@ -143,12 +170,17 @@ mod tests {
|
|||||||
value: Value::Text("v1".to_string()),
|
value: Value::Text("v1".to_string()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
catalog.items.insert("e_new".to_string(), new_entity);
|
let _ = catalog.on_items(|items| {
|
||||||
|
items.insert("e_new".to_string(), new_entity);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
// Corrupt the DB to cause a failure during the transaction
|
// Corrupt the DB to cause a failure during the transaction
|
||||||
conn.execute("DROP TABLE attribute", []).unwrap();
|
conn.execute("DROP TABLE attribute", []).unwrap();
|
||||||
drop(conn);
|
drop(conn);
|
||||||
let result = sqlite::persist::catalog(db_path, &catalog);
|
let is_err = catalog
|
||||||
assert!(result.is_err());
|
.with_items(|items| Ok(sqlite::persist::catalog(db_path, items).is_err()))
|
||||||
|
.unwrap();
|
||||||
|
assert!(is_err);
|
||||||
// Re-open connection to check state
|
// Re-open connection to check state
|
||||||
let conn = Connection::open(db_path).unwrap();
|
let conn = Connection::open(db_path).unwrap();
|
||||||
// The new entity should not have been inserted due to rollback
|
// The new entity should not have been inserted due to rollback
|
||||||
|
|||||||
Reference in New Issue
Block a user