Changeset View
Changeset View
Standalone View
Standalone View
services/comm-services-lib/src/database.rs
use aws_sdk_dynamodb::{model::AttributeValue, Error as DynamoDBError}; | use aws_sdk_dynamodb::{model::AttributeValue, Error as DynamoDBError}; | ||||
use chrono::{DateTime, Utc}; | use chrono::{DateTime, Utc}; | ||||
use std::collections::HashMap; | |||||
use std::fmt::{Display, Formatter}; | use std::fmt::{Display, Formatter}; | ||||
use std::num::ParseIntError; | |||||
#[derive( | #[derive( | ||||
Debug, derive_more::Display, derive_more::From, derive_more::Error, | Debug, derive_more::Display, derive_more::From, derive_more::Error, | ||||
)] | )] | ||||
pub enum Error { | pub enum Error { | ||||
#[display(...)] | #[display(...)] | ||||
AwsSdk(DynamoDBError), | AwsSdk(DynamoDBError), | ||||
#[display(...)] | #[display(...)] | ||||
Attribute(DBItemError), | Attribute(DBItemError), | ||||
} | } | ||||
#[derive(Debug)] | |||||
pub enum Value { | |||||
AttributeValue(Option<AttributeValue>), | |||||
String(String), | |||||
} | |||||
#[derive(Debug, derive_more::Error, derive_more::Constructor)] | #[derive(Debug, derive_more::Error, derive_more::Constructor)] | ||||
pub struct DBItemError { | pub struct DBItemError { | ||||
attribute_name: &'static str, | attribute_name: &'static str, | ||||
attribute_value: Option<AttributeValue>, | attribute_value: Value, | ||||
attribute_error: DBItemAttributeError, | attribute_error: DBItemAttributeError, | ||||
} | } | ||||
impl Display for DBItemError { | impl Display for DBItemError { | ||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { | ||||
match &self.attribute_error { | match &self.attribute_error { | ||||
DBItemAttributeError::Missing => { | DBItemAttributeError::Missing => { | ||||
write!(f, "Attribute {} is missing", self.attribute_name) | write!(f, "Attribute {} is missing", self.attribute_name) | ||||
Show All 15 Lines | |||||
#[derive(Debug, derive_more::Display, derive_more::Error)] | #[derive(Debug, derive_more::Display, derive_more::Error)] | ||||
pub enum DBItemAttributeError { | pub enum DBItemAttributeError { | ||||
#[display(...)] | #[display(...)] | ||||
Missing, | Missing, | ||||
#[display(...)] | #[display(...)] | ||||
IncorrectType, | IncorrectType, | ||||
#[display(...)] | #[display(...)] | ||||
InvalidTimestamp(chrono::ParseError), | InvalidTimestamp(chrono::ParseError), | ||||
#[display(...)] | |||||
InvalidNumberFormat(ParseIntError), | |||||
} | } | ||||
pub fn parse_string_attribute( | pub fn parse_string_attribute( | ||||
attribute_name: &'static str, | attribute_name: &'static str, | ||||
attribute_value: Option<AttributeValue>, | attribute_value: Option<AttributeValue>, | ||||
) -> Result<String, DBItemError> { | ) -> Result<String, DBItemError> { | ||||
match attribute_value { | match attribute_value { | ||||
Some(AttributeValue::S(value)) => Ok(value), | Some(AttributeValue::S(value)) => Ok(value), | ||||
Some(_) => Err(DBItemError::new( | Some(_) => Err(DBItemError::new( | ||||
attribute_name, | attribute_name, | ||||
attribute_value, | Value::AttributeValue(attribute_value), | ||||
DBItemAttributeError::IncorrectType, | DBItemAttributeError::IncorrectType, | ||||
)), | )), | ||||
None => Err(DBItemError::new( | None => Err(DBItemError::new( | ||||
attribute_name, | attribute_name, | ||||
attribute_value, | Value::AttributeValue(attribute_value), | ||||
DBItemAttributeError::Missing, | DBItemAttributeError::Missing, | ||||
)), | )), | ||||
} | } | ||||
} | } | ||||
pub fn parse_bool_attribute( | pub fn parse_bool_attribute( | ||||
attribute_name: &'static str, | attribute_name: &'static str, | ||||
attribute_value: Option<AttributeValue>, | attribute_value: Option<AttributeValue>, | ||||
) -> Result<bool, DBItemError> { | ) -> Result<bool, DBItemError> { | ||||
match attribute_value { | match attribute_value { | ||||
Some(AttributeValue::Bool(value)) => Ok(value), | Some(AttributeValue::Bool(value)) => Ok(value), | ||||
Some(_) => Err(DBItemError::new( | Some(_) => Err(DBItemError::new( | ||||
attribute_name, | attribute_name, | ||||
attribute_value, | Value::AttributeValue(attribute_value), | ||||
DBItemAttributeError::IncorrectType, | DBItemAttributeError::IncorrectType, | ||||
)), | )), | ||||
None => Err(DBItemError::new( | None => Err(DBItemError::new( | ||||
attribute_name, | attribute_name, | ||||
attribute_value, | Value::AttributeValue(attribute_value), | ||||
DBItemAttributeError::Missing, | DBItemAttributeError::Missing, | ||||
)), | )), | ||||
} | } | ||||
} | } | ||||
pub fn parse_datetime_attribute( | pub fn parse_datetime_attribute( | ||||
attribute_name: &'static str, | attribute_name: &'static str, | ||||
attribute_value: Option<AttributeValue>, | attribute_value: Option<AttributeValue>, | ||||
) -> Result<DateTime<Utc>, DBItemError> { | ) -> Result<DateTime<Utc>, DBItemError> { | ||||
if let Some(AttributeValue::S(datetime)) = &attribute_value { | if let Some(AttributeValue::S(datetime)) = &attribute_value { | ||||
// parse() accepts a relaxed RFC3339 string | // parse() accepts a relaxed RFC3339 string | ||||
datetime.parse().map_err(|e| { | datetime.parse().map_err(|e| { | ||||
DBItemError::new( | DBItemError::new( | ||||
attribute_name, | attribute_name, | ||||
attribute_value, | Value::AttributeValue(attribute_value), | ||||
DBItemAttributeError::InvalidTimestamp(e), | DBItemAttributeError::InvalidTimestamp(e), | ||||
) | ) | ||||
}) | }) | ||||
} else { | } else { | ||||
Err(DBItemError::new( | Err(DBItemError::new( | ||||
attribute_name, | attribute_name, | ||||
attribute_value, | Value::AttributeValue(attribute_value), | ||||
DBItemAttributeError::Missing, | DBItemAttributeError::Missing, | ||||
)) | )) | ||||
} | } | ||||
} | } | ||||
pub fn parse_map_attribute( | |||||
attribute_name: &'static str, | |||||
attribute_value: Option<AttributeValue>, | |||||
) -> Result<HashMap<String, AttributeValue>, DBItemError> { | |||||
match attribute_value { | |||||
Some(AttributeValue::M(map)) => Ok(map), | |||||
Some(_) => Err(DBItemError::new( | |||||
attribute_name, | |||||
Value::AttributeValue(attribute_value), | |||||
DBItemAttributeError::IncorrectType, | |||||
)), | |||||
None => Err(DBItemError::new( | |||||
attribute_name, | |||||
Value::AttributeValue(attribute_value), | |||||
DBItemAttributeError::Missing, | |||||
)), | |||||
} | |||||
} | |||||
pub fn parse_number( | |||||
attribute_name: &'static str, | |||||
attribute_value: &str, | |||||
) -> Result<i32, DBItemError> { | |||||
attribute_value.parse::<i32>().map_err(|e| { | |||||
DBItemError::new( | |||||
attribute_name, | |||||
Value::String(attribute_value.to_string()), | |||||
DBItemAttributeError::InvalidNumberFormat(e), | |||||
) | |||||
}) | |||||
} |