review: change trait req for entity unwrap* functions to TryFrom<Value> to handle errors

This commit is contained in:
2025-10-17 09:03:21 +02:00
parent 05dc429d86
commit 58e51f4f47
19 changed files with 132 additions and 118 deletions

View File

@@ -46,11 +46,11 @@ impl From<Entity> for Product {
// Set the product's ID from the entity's ID.
id: value.id.clone(),
// Unwrap the "name" attribute to get the product's name.
name: value.unwrap("name"),
name: value.unwrap("name").expect("name is always present"),
// Unwrap the optional "model" attribute to get the product's model.
model: value.unwrap_opt("model"),
model: value.unwrap_opt("model").expect("model is always present"),
// Unwrap the "price" attribute to get the product's price.
price: value.unwrap("price"),
price: value.unwrap("price").expect("price is always present"),
}
}
}

View File

@@ -1,10 +0,0 @@
use crate::*;
impl From<Value> for bool {
fn from(value: Value) -> bool {
match value {
Value::Bool(value) => value,
_ => panic!("Type mismatch"),
}
}
}

View File

@@ -0,0 +1,11 @@
use crate::*;
impl TryFrom<Value> for bool {
type Error = ();
fn try_from(value: Value) -> Result<bool, Self::Error> {
match value {
Value::Bool(value) => Ok(value),
_ => Err(()),
}
}
}

View File

@@ -1,10 +0,0 @@
use crate::*;
impl From<Value> for f64 {
fn from(value: Value) -> f64 {
match value {
Value::Real(value) => value,
_ => panic!("Type mismatch"),
}
}
}

View File

@@ -0,0 +1,11 @@
use crate::*;
impl TryFrom<Value> for f64 {
type Error = ();
fn try_from(value: Value) -> Result<f64, Self::Error> {
match value {
Value::Real(value) => Ok(value),
_ => Err(()),
}
}
}

View File

@@ -1,13 +0,0 @@
use crate::*;
impl From<Value> for i32 {
fn from(value: Value) -> i32 {
match value {
Value::SignedInt(value) => value
.try_into()
.map_err(|_| "Type mismatch".to_string())
.unwrap(),
_ => panic!("Type mismatch"),
}
}
}

View File

@@ -0,0 +1,11 @@
use crate::*;
impl TryFrom<Value> for i32 {
type Error = ();
fn try_from(value: Value) -> Result<i32, Self::Error> {
match value {
Value::SignedInt(value) => value.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}

View File

@@ -1,10 +0,0 @@
use crate::*;
impl From<Value> for i64 {
fn from(value: Value) -> i64 {
match value {
Value::SignedInt(value) => value,
_ => panic!("Type mismatch"),
}
}
}

View File

@@ -0,0 +1,11 @@
use crate::*;
impl TryFrom<Value> for i64 {
type Error = ();
fn try_from(value: Value) -> Result<i64, Self::Error> {
match value {
Value::SignedInt(value) => Ok(value),
_ => Err(()),
}
}
}

View File

@@ -1,10 +1,10 @@
pub mod bool_from_value;
pub mod f64_from_value;
pub mod i32_from_value;
pub mod i64_from_value;
pub mod string_from_value;
pub mod u32_from_value;
pub mod u64_from_value;
pub mod bool_try_from_value;
pub mod f64_try_from_value;
pub mod i32_try_from_value;
pub mod i64_try_from_value;
pub mod string_try_from_value;
pub mod u32_try_from_value;
pub mod u64_try_from_value;
pub mod value_from_bool;
pub mod value_from_f64;
pub mod value_from_i32;

View File

@@ -1,10 +0,0 @@
use crate::*;
impl From<Value> for String {
fn from(value: Value) -> String {
match value {
Value::Text(value) => value,
_ => panic!("Type mismatch"),
}
}
}

View File

@@ -0,0 +1,11 @@
use crate::*;
impl TryFrom<Value> for String {
type Error = ();
fn try_from(value: Value) -> Result<String, Self::Error> {
match value {
Value::Text(value) => Ok(value),
_ => Err(()),
}
}
}

View File

@@ -1,13 +0,0 @@
use crate::*;
impl From<Value> for u32 {
fn from(value: Value) -> u32 {
match value {
Value::UnsignedInt(value) => value
.try_into()
.map_err(|_| "Type mismatch".to_string())
.unwrap(),
_ => panic!("Type mismatch"),
}
}
}

View File

@@ -0,0 +1,11 @@
use crate::*;
impl TryFrom<Value> for u32 {
type Error = ();
fn try_from(value: Value) -> Result<u32, Self::Error> {
match value {
Value::UnsignedInt(value) => value.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}

View File

@@ -1,10 +0,0 @@
use crate::*;
impl From<Value> for u64 {
fn from(value: Value) -> u64 {
match value {
Value::UnsignedInt(value) => value,
_ => panic!("Type mismatch"),
}
}
}

View File

@@ -0,0 +1,11 @@
use crate::*;
impl TryFrom<Value> for u64 {
type Error = ();
fn try_from(value: Value) -> Result<u64, Self::Error> {
match value {
Value::UnsignedInt(value) => Ok(value),
_ => Err(()),
}
}
}

View File

@@ -224,9 +224,11 @@ mod tests {
fn from(entity: Entity) -> Self {
Self {
id: entity.id.clone(),
name: entity.unwrap("name"),
price: entity.unwrap("price"),
in_stock: entity.unwrap("in_stock"),
name: entity.unwrap("name").expect("name is always present"),
price: entity.unwrap("price").expect("price is always present"),
in_stock: entity
.unwrap("in_stock")
.expect("in_stock is always present"),
}
}
}

View File

@@ -101,9 +101,7 @@ impl Entity {
/// Unwraps an attribute's value into a specified type `T`.
///
/// # Panics
///
/// Panics if the attribute does not exist.
/// This function requires `T` to implement `TryFrom<Value>`.
///
/// # Arguments
///
@@ -111,48 +109,59 @@ impl Entity {
///
/// # Returns
///
/// The value of the attribute converted to type `T`.
pub fn unwrap<T>(&self, id: &str) -> T
/// A `Result<T, FailedTo>` which is `Ok(T)` if the conversion is successful,
/// or `Err(FailedTo::ConvertValue)` if it fails.
pub fn unwrap<T>(&self, id: &str) -> Result<T, FailedTo>
where
T: From<Value>,
T: TryFrom<Value>,
{
self.value_of(id)
.map(|value| T::from(value.clone()))
.map(|value| T::try_from(value.clone()).map_err(|_| FailedTo::ConvertValue))
.unwrap()
}
/// Unwraps an attribute's value into an `Option<T>`.
///
/// This function requires `T` to implement `TryFrom<Value>`.
///
/// # Arguments
///
/// * `id` - The ID of the attribute to unwrap.
///
/// # Returns
///
/// An `Option<T>` containing the converted value if the attribute exists, otherwise `None`.
pub fn unwrap_opt<T>(&self, id: &str) -> Option<T>
/// A `Result<Option<T>, FailedTo>`.
/// - `Ok(Some(T))` if the attribute exists and the conversion is successful.
/// - `Ok(None)` if the attribute does not exist.
/// - `Err(FailedTo::ConvertValue)` if the attribute exists but the conversion fails.
pub fn unwrap_opt<T>(&self, id: &str) -> Result<Option<T>, FailedTo>
where
T: From<Value>,
{
self.value_of(id).map(|value| T::from(value.clone()))
}
/// Unwraps an attribute's value, returning a default if it doesn't exist.
///
/// # Arguments
///
/// * `id` - The ID of the attribute to unwrap.
/// * `default` - The default value to return if the attribute is not found.
///
/// # Returns
///
/// The converted value of the attribute or the default value.
pub fn unwrap_or<T>(&self, id: &str, default: T) -> T
where
T: From<Value>,
T: TryFrom<Value>,
{
self.value_of(id)
.map(|value| T::from(value.clone()))
.unwrap_or(default)
.map(|value| T::try_from(value.clone()).map_err(|_| FailedTo::ConvertValue))
.transpose()
}
/// Unwraps an attribute's value, returning a default value if it doesn't exist or fails to convert.
///
/// This function requires `T` to implement `TryFrom<Value>`.
///
/// # Arguments
///
/// * `id` - The ID of the attribute to unwrap.
/// * `default` - The default value to return if the attribute is not found or conversion fails.
///
/// # Returns
///
/// The converted value of the attribute, or the default value.
pub fn unwrap_or<T>(&self, id: &str, default: T) -> Result<T, FailedTo>
where
T: TryFrom<Value>,
{
self.value_of(id)
.map(|value| T::try_from(value.clone()).map_err(|_| FailedTo::ConvertValue))
.transpose()
.map(|value| value.unwrap_or(default))
}
}

View File

@@ -3,6 +3,8 @@ use crate::*;
/// Represents the possible failures that can occur in the library.
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)]
pub enum FailedTo {
/// Failed to convert from Value to type.
ConvertValue,
/// Failed to initialize the database.
InitDatabase,
/// Failed to load data from the database.