Compare commits
8 Commits
d5497906be
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
| 36a8b5100e | |||
| cb3d07ec26 | |||
| e98f6b7edf | |||
| 66e7bc22cf | |||
| 35f83c376f | |||
| 3f3a2df072 | |||
| c3d4e63d73 | |||
| 62582ee055 |
@@ -11,9 +11,7 @@ code_coverage:
|
|||||||
cd 01.workspace && cargo llvm-cov clean && cargo llvm-cov --show-missing-lines
|
cd 01.workspace && cargo llvm-cov clean && cargo llvm-cov --show-missing-lines
|
||||||
|
|
||||||
force_sync_remotes:
|
force_sync_remotes:
|
||||||
git push --force codeberg.org next
|
|
||||||
git push --force github.com next
|
git push --force github.com next
|
||||||
|
|
||||||
sync_remotes:
|
sync_remotes:
|
||||||
git push codeberg.org next
|
git push github next
|
||||||
git push github.com next
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "heave"
|
name = "heave"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
description = "An Entity-Attribute-Value (EAV) system library for Rust, backed by SQLite."
|
description = "An Entity-Attribute-Value (EAV) system library for Rust, backed by SQLite."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
keywords = ["eav", "entity-attribute-value", "sqlite", "database", "orm", "data-model", "rust"]
|
keywords = ["eav", "entity-attribute-value", "sqlite", "database", "orm", "data-model", "rust"]
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ impl From<Entity> for Product {
|
|||||||
// Create a new `Product` from the entity's attributes.
|
// Create a new `Product` from the entity's attributes.
|
||||||
Self {
|
Self {
|
||||||
// Set the product's ID from the entity's ID.
|
// Set the product's ID from the entity's ID.
|
||||||
id: value.id(),
|
id: value.id().to_string(),
|
||||||
// Unwrap the "name" attribute to get the product's name.
|
// Unwrap the "name" attribute to get the product's name.
|
||||||
name: value.unwrap("name").expect("name is always present"),
|
name: value.unwrap("name").expect("name is always present"),
|
||||||
// Unwrap the optional "model" attribute to get the product's model.
|
// Unwrap the optional "model" attribute to get the product's model.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ impl From<Entity> for Component {
|
|||||||
// `from` is a function that converts an `Entity` into a `Component`.
|
// `from` is a function that converts an `Entity` into a `Component`.
|
||||||
fn from(value: Entity) -> Self {
|
fn from(value: Entity) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: value.id(),
|
id: value.id().to_string(),
|
||||||
part_number: value
|
part_number: value
|
||||||
.unwrap("part_number")
|
.unwrap("part_number")
|
||||||
.expect("part_number is always present"),
|
.expect("part_number is always present"),
|
||||||
|
|||||||
@@ -41,18 +41,18 @@ impl From<Entity> for Product {
|
|||||||
if let Some(ref subclass) = value.subclass() {
|
if let Some(ref subclass) = value.subclass() {
|
||||||
match subclass.as_ref() {
|
match subclass.as_ref() {
|
||||||
"laptop" => Product::Laptop(Laptop {
|
"laptop" => Product::Laptop(Laptop {
|
||||||
id: value.id(),
|
id: value.id().to_string(),
|
||||||
model: value.unwrap("model").expect("model is mandatory"),
|
model: value.unwrap("model").expect("model is mandatory"),
|
||||||
price: value.unwrap("price").expect("price is mandatory"),
|
price: value.unwrap("price").expect("price is mandatory"),
|
||||||
}),
|
}),
|
||||||
"display" => Product::Display(Display {
|
"display" => Product::Display(Display {
|
||||||
id: value.id(),
|
id: value.id().to_string(),
|
||||||
model: value.unwrap("model").expect("model is mandatory"),
|
model: value.unwrap("model").expect("model is mandatory"),
|
||||||
price: value.unwrap("price").expect("price is mandatory"),
|
price: value.unwrap("price").expect("price is mandatory"),
|
||||||
resolution: value.unwrap("resolution").expect("resolution is mandatory"),
|
resolution: value.unwrap("resolution").expect("resolution is mandatory"),
|
||||||
}),
|
}),
|
||||||
"mouse" => Product::Mouse(Mouse {
|
"mouse" => Product::Mouse(Mouse {
|
||||||
id: value.id(),
|
id: value.id().to_string(),
|
||||||
model: value.unwrap("model").expect("model is mandatory"),
|
model: value.unwrap("model").expect("model is mandatory"),
|
||||||
price: value.unwrap("price").expect("price is mandatory"),
|
price: value.unwrap("price").expect("price is mandatory"),
|
||||||
wireless: value.unwrap("wireless").expect("wireless is mandatory"),
|
wireless: value.unwrap("wireless").expect("wireless is mandatory"),
|
||||||
|
|||||||
@@ -30,12 +30,8 @@ fn from_condition(
|
|||||||
(_, Condition::Bool(_)) => return Err(FailedTo::ComposeFilter),
|
(_, Condition::Bool(_)) => return Err(FailedTo::ComposeFilter),
|
||||||
// SIGNED INT
|
// SIGNED INT
|
||||||
(Comparison::Equal, Condition::SignedInt(_)) => compose_fragment("value_int", "=", i),
|
(Comparison::Equal, Condition::SignedInt(_)) => compose_fragment("value_int", "=", i),
|
||||||
(Comparison::Greater, Condition::SignedInt(_)) => {
|
(Comparison::Greater, Condition::SignedInt(_)) => compose_fragment("value_int", ">", i),
|
||||||
compose_fragment("value_int", ">", i)
|
(Comparison::Lesser, Condition::SignedInt(_)) => compose_fragment("value_int", "<", i),
|
||||||
}
|
|
||||||
(Comparison::Lesser, Condition::SignedInt(_)) => {
|
|
||||||
compose_fragment("value_int", "<", i)
|
|
||||||
}
|
|
||||||
(Comparison::GreaterOrEqual, Condition::SignedInt(_)) => {
|
(Comparison::GreaterOrEqual, Condition::SignedInt(_)) => {
|
||||||
compose_fragment("value_int", ">=", i)
|
compose_fragment("value_int", ">=", i)
|
||||||
}
|
}
|
||||||
@@ -44,15 +40,9 @@ fn from_condition(
|
|||||||
}
|
}
|
||||||
(_, Condition::SignedInt(_)) => return Err(FailedTo::ComposeFilter),
|
(_, Condition::SignedInt(_)) => return Err(FailedTo::ComposeFilter),
|
||||||
// UNSIGNED INT
|
// UNSIGNED INT
|
||||||
(Comparison::Equal, Condition::UnsignedInt(_)) => {
|
(Comparison::Equal, Condition::UnsignedInt(_)) => compose_fragment("value_uint", "=", i),
|
||||||
compose_fragment("value_uint", "=", i)
|
(Comparison::Greater, Condition::UnsignedInt(_)) => compose_fragment("value_uint", ">", i),
|
||||||
}
|
(Comparison::Lesser, Condition::UnsignedInt(_)) => compose_fragment("value_uint", "<", i),
|
||||||
(Comparison::Greater, Condition::UnsignedInt(_)) => {
|
|
||||||
compose_fragment("value_uint", ">", i)
|
|
||||||
}
|
|
||||||
(Comparison::Lesser, Condition::UnsignedInt(_)) => {
|
|
||||||
compose_fragment("value_uint", "<", i)
|
|
||||||
}
|
|
||||||
(Comparison::GreaterOrEqual, Condition::UnsignedInt(_)) => {
|
(Comparison::GreaterOrEqual, Condition::UnsignedInt(_)) => {
|
||||||
compose_fragment("value_uint", ">=", i)
|
compose_fragment("value_uint", ">=", i)
|
||||||
}
|
}
|
||||||
@@ -64,12 +54,8 @@ fn from_condition(
|
|||||||
(Comparison::Equal, Condition::Real(_)) => compose_fragment("value_real", "=", i),
|
(Comparison::Equal, Condition::Real(_)) => compose_fragment("value_real", "=", i),
|
||||||
(Comparison::Greater, Condition::Real(_)) => compose_fragment("value_real", ">", i),
|
(Comparison::Greater, Condition::Real(_)) => compose_fragment("value_real", ">", i),
|
||||||
(Comparison::Lesser, Condition::Real(_)) => compose_fragment("value_real", "<", i),
|
(Comparison::Lesser, Condition::Real(_)) => compose_fragment("value_real", "<", i),
|
||||||
(Comparison::GreaterOrEqual, Condition::Real(_)) => {
|
(Comparison::GreaterOrEqual, Condition::Real(_)) => compose_fragment("value_real", ">=", i),
|
||||||
compose_fragment("value_real", ">=", i)
|
(Comparison::LesserOrEqual, Condition::Real(_)) => compose_fragment("value_real", "<=", i),
|
||||||
}
|
|
||||||
(Comparison::LesserOrEqual, Condition::Real(_)) => {
|
|
||||||
compose_fragment("value_real", "<=", i)
|
|
||||||
}
|
|
||||||
(_, Condition::Real(_)) => return Err(FailedTo::ComposeFilter),
|
(_, Condition::Real(_)) => return Err(FailedTo::ComposeFilter),
|
||||||
// TEXT
|
// TEXT
|
||||||
(Comparison::IsExactly, Condition::Text(_)) => compose_fragment("value_text", "=", i),
|
(Comparison::IsExactly, Condition::Text(_)) => compose_fragment("value_text", "=", i),
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
//!
|
//!
|
||||||
//! 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(),
|
//! id: entity.id().to_string(),
|
||||||
//! 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)?,
|
||||||
|
|||||||
@@ -141,14 +141,15 @@ impl Entity {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A `Result<T, FailedTo>` which is `Ok(T)` if the conversion is successful,
|
/// A `Result<T, FailedTo>` which is `Ok(T)` if the conversion is successful,
|
||||||
/// or `Err(FailedTo::ConvertValue)` if it fails.
|
/// or `Err(FailedTo::ConvertValue)` if the conversion fails,
|
||||||
|
/// or `Err(FailedTo::FindAttribute)` if the attribute doesn't exist.
|
||||||
pub fn unwrap<T>(&self, id: &str) -> Result<T, FailedTo>
|
pub fn unwrap<T>(&self, id: &str) -> Result<T, FailedTo>
|
||||||
where
|
where
|
||||||
T: TryFrom<Value>,
|
T: TryFrom<Value>,
|
||||||
{
|
{
|
||||||
self.value_of(id)
|
self.value_of(id)
|
||||||
.map(|value| T::try_from(value.clone()).map_err(|_| FailedTo::ConvertValue))
|
.map(|value| T::try_from(value.clone()).map_err(|_| FailedTo::ConvertValue))
|
||||||
.unwrap()
|
.unwrap_or(Err(FailedTo::FindAttribute))
|
||||||
}
|
}
|
||||||
/// Unwraps an attribute's value into an `Option<T>`.
|
/// Unwraps an attribute's value into an `Option<T>`.
|
||||||
///
|
///
|
||||||
@@ -194,8 +195,8 @@ impl Entity {
|
|||||||
.map(|value| value.unwrap_or(default))
|
.map(|value| value.unwrap_or(default))
|
||||||
}
|
}
|
||||||
/// Returns the ID of the entity.
|
/// Returns the ID of the entity.
|
||||||
pub fn id(&self) -> String {
|
pub fn id(&self) -> &str {
|
||||||
self.id.clone()
|
&self.id
|
||||||
}
|
}
|
||||||
/// Returns the subclass of the entity, if it has one.
|
/// Returns the subclass of the entity, if it has one.
|
||||||
pub fn subclass(&self) -> Option<String> {
|
pub fn subclass(&self) -> Option<String> {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ pub enum FailedTo {
|
|||||||
ConvertValue,
|
ConvertValue,
|
||||||
/// Failed to execute predicate to mutate an item.
|
/// Failed to execute predicate to mutate an item.
|
||||||
ExecutePredicate(Vec<Box<dyn error::Error>>),
|
ExecutePredicate(Vec<Box<dyn error::Error>>),
|
||||||
|
/// Failed to find given attribute.
|
||||||
|
FindAttribute,
|
||||||
/// Failed to initialize the database.
|
/// Failed to initialize the database.
|
||||||
InitDatabase,
|
InitDatabase,
|
||||||
/// Failed to load data from the database.
|
/// Failed to load data from the database.
|
||||||
@@ -38,6 +40,7 @@ impl PartialEq for FailedTo {
|
|||||||
| (FailedTo::ConvertObject, FailedTo::ConvertObject)
|
| (FailedTo::ConvertObject, FailedTo::ConvertObject)
|
||||||
| (FailedTo::ConvertValue, FailedTo::ConvertValue)
|
| (FailedTo::ConvertValue, FailedTo::ConvertValue)
|
||||||
| (FailedTo::ExecutePredicate(_), FailedTo::ExecutePredicate(_))
|
| (FailedTo::ExecutePredicate(_), FailedTo::ExecutePredicate(_))
|
||||||
|
| (FailedTo::FindAttribute, FailedTo::FindAttribute)
|
||||||
| (FailedTo::InitDatabase, FailedTo::InitDatabase)
|
| (FailedTo::InitDatabase, FailedTo::InitDatabase)
|
||||||
| (FailedTo::LoadFromDB, FailedTo::LoadFromDB)
|
| (FailedTo::LoadFromDB, FailedTo::LoadFromDB)
|
||||||
| (FailedTo::LockCatalog, FailedTo::LockCatalog)
|
| (FailedTo::LockCatalog, FailedTo::LockCatalog)
|
||||||
@@ -54,3 +57,36 @@ impl From<sqlite::FailedTo> for FailedTo {
|
|||||||
Self::SQLite(value)
|
Self::SQLite(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for FailedTo {}
|
||||||
|
|
||||||
|
impl std::fmt::Display for FailedTo {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
FailedTo::ComposeFilter => write!(f, "Failed to compose filter statement"),
|
||||||
|
FailedTo::ConvertEntity => write!(f, "Failed to convert from Entity to type"),
|
||||||
|
FailedTo::ConvertObject => write!(f, "Failed to convert from type to Entity"),
|
||||||
|
FailedTo::ConvertValue => write!(f, "Failed to convert from Value to type"),
|
||||||
|
FailedTo::ExecutePredicate(errors) => {
|
||||||
|
write!(f, "Failed to execute predicate: ")?;
|
||||||
|
for (i, err) in errors.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(f, "; ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", err)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
FailedTo::FindAttribute => write!(f, "Failed to find given attribute"),
|
||||||
|
FailedTo::InitDatabase => write!(f, "Failed to initialize the database"),
|
||||||
|
FailedTo::LoadFromDB => write!(f, "Failed to load data from the database"),
|
||||||
|
FailedTo::LockCatalog => {
|
||||||
|
write!(f, "Failed to lock catalog in a multithread environment")
|
||||||
|
}
|
||||||
|
FailedTo::MapAttribute => write!(f, "Failed to map a database row to an attribute"),
|
||||||
|
FailedTo::MapEntity => write!(f, "Failed to map a database row to an entity"),
|
||||||
|
FailedTo::PersistCatalog => write!(f, "Failed to persist the catalog to the database"),
|
||||||
|
FailedTo::SQLite(e) => write!(f, "SQLite error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ impl From<Item> for Entity {
|
|||||||
impl From<Entity> for Item {
|
impl From<Entity> for Item {
|
||||||
fn from(entity: Entity) -> Self {
|
fn from(entity: Entity) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: entity.id(),
|
id: entity.id().to_string(),
|
||||||
first_seen: entity.ref_date.expect("ref date is always present"),
|
first_seen: entity.ref_date.expect("ref date is always present"),
|
||||||
name: entity.unwrap("name").expect("name is always present"),
|
name: entity.unwrap("name").expect("name is always present"),
|
||||||
price: entity.unwrap("price").expect("price is always present"),
|
price: entity.unwrap("price").expect("price is always present"),
|
||||||
|
|||||||
@@ -18,3 +18,33 @@ pub enum FailedTo {
|
|||||||
/// Failed to prepare a SQL statement for execution.
|
/// Failed to prepare a SQL statement for execution.
|
||||||
PrepareStatement(rusqlite::Error),
|
PrepareStatement(rusqlite::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for FailedTo {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
FailedTo::BeginTransaction(e) => Some(e),
|
||||||
|
FailedTo::BuildStatement => None,
|
||||||
|
FailedTo::CommitTransaction(e) => Some(e),
|
||||||
|
FailedTo::ExecuteBatch(e) => Some(e),
|
||||||
|
FailedTo::ExecuteQuery(e) => Some(e),
|
||||||
|
FailedTo::ExecuteStatement(e) => Some(e),
|
||||||
|
FailedTo::OpenConnection(e) => Some(e),
|
||||||
|
FailedTo::PrepareStatement(e) => Some(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for FailedTo {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
FailedTo::BeginTransaction(e) => write!(f, "Failed to begin transaction: {}", e),
|
||||||
|
FailedTo::BuildStatement => write!(f, "Failed to build SQL statement"),
|
||||||
|
FailedTo::CommitTransaction(e) => write!(f, "Failed to commit transaction: {}", e),
|
||||||
|
FailedTo::ExecuteBatch(e) => write!(f, "Failed to execute batch: {}", e),
|
||||||
|
FailedTo::ExecuteQuery(e) => write!(f, "Failed to execute query: {}", e),
|
||||||
|
FailedTo::ExecuteStatement(e) => write!(f, "Failed to execute statement: {}", e),
|
||||||
|
FailedTo::OpenConnection(e) => write!(f, "Failed to open connection: {}", e),
|
||||||
|
FailedTo::PrepareStatement(e) => write!(f, "Failed to prepare statement: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user