use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use crate::proxy::builder::{GetterFn, SetterFn, ValidatorFn};
use crate::proxy::{ProxyError, ProxyResult};
pub struct AssociationProxy<T, U> {
pub name: Option<String>,
pub relationship: String,
pub attribute: String,
pub creator: Option<fn(U) -> T>,
pub getter: Option<GetterFn<T, U>>,
pub setter: Option<SetterFn<T, U>>,
pub validator: Option<ValidatorFn<U>>,
pub transform: Option<fn(U) -> U>,
_phantom: PhantomData<(T, U)>,
}
impl<T, U> AssociationProxy<T, U> {
pub fn new(relationship: &str, attribute: &str) -> Self {
Self {
name: None,
relationship: relationship.to_string(),
attribute: attribute.to_string(),
creator: None,
getter: None,
setter: None,
validator: None,
transform: None,
_phantom: PhantomData,
}
}
pub fn with_creator(mut self, creator: fn(U) -> T) -> Self {
self.creator = Some(creator);
self
}
pub fn with_getter(mut self, getter: fn(&T) -> Result<U, crate::proxy::ProxyError>) -> Self {
self.getter = Some(getter);
self
}
pub fn with_setter(
mut self,
setter: fn(&mut T, U) -> Result<(), crate::proxy::ProxyError>,
) -> Self {
self.setter = Some(setter);
self
}
pub fn with_validator(
mut self,
validator: fn(&U) -> Result<(), crate::proxy::ProxyError>,
) -> Self {
self.validator = Some(validator);
self
}
pub fn with_transform(mut self, transform: fn(U) -> U) -> Self {
self.transform = Some(transform);
self
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn relationship(&self) -> &str {
&self.relationship
}
pub fn attribute(&self) -> &str {
&self.attribute
}
pub fn has_custom_accessors(&self) -> bool {
self.getter.is_some() || self.setter.is_some()
}
pub fn has_validator(&self) -> bool {
self.validator.is_some()
}
pub fn has_transform(&self) -> bool {
self.transform.is_some()
}
}
#[async_trait]
pub trait ProxyAccessor<T> {
async fn get(&self, source: &T) -> ProxyResult<ProxyTarget>;
async fn set(&self, source: &mut T, value: ProxyTarget) -> ProxyResult<()>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ProxyTarget {
Scalar(ScalarValue),
Collection(Vec<ScalarValue>),
None,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum ScalarValue {
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Null,
}
impl From<String> for ScalarValue {
fn from(s: String) -> Self {
ScalarValue::String(s)
}
}
impl From<&str> for ScalarValue {
fn from(s: &str) -> Self {
ScalarValue::String(s.to_string())
}
}
impl From<i64> for ScalarValue {
fn from(i: i64) -> Self {
ScalarValue::Integer(i)
}
}
impl From<f64> for ScalarValue {
fn from(f: f64) -> Self {
ScalarValue::Float(f)
}
}
impl From<bool> for ScalarValue {
fn from(b: bool) -> Self {
ScalarValue::Boolean(b)
}
}
impl ScalarValue {
pub fn as_string(&self) -> ProxyResult<String> {
match self {
ScalarValue::String(s) => Ok(s.clone()),
_ => Err(ProxyError::TypeMismatch {
expected: "String".to_string(),
actual: format!("{:?}", self),
}),
}
}
pub fn as_integer(&self) -> ProxyResult<i64> {
match self {
ScalarValue::Integer(i) => Ok(*i),
_ => Err(ProxyError::TypeMismatch {
expected: "Integer".to_string(),
actual: format!("{:?}", self),
}),
}
}
pub fn as_float(&self) -> ProxyResult<f64> {
match self {
ScalarValue::Float(f) => Ok(*f),
_ => Err(ProxyError::TypeMismatch {
expected: "Float".to_string(),
actual: format!("{:?}", self),
}),
}
}
pub fn as_boolean(&self) -> ProxyResult<bool> {
match self {
ScalarValue::Boolean(b) => Ok(*b),
_ => Err(ProxyError::TypeMismatch {
expected: "Boolean".to_string(),
actual: format!("{:?}", self),
}),
}
}
pub fn is_null(&self) -> bool {
matches!(self, ScalarValue::Null)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_proxy_scalar_conversions_unit() {
let s = ScalarValue::String("test".to_string());
assert_eq!(s.as_string().unwrap(), "test");
let i = ScalarValue::Integer(42);
assert_eq!(i.as_integer().unwrap(), 42);
let f = ScalarValue::Float(3.15);
assert_eq!(f.as_float().unwrap(), 3.15);
let b = ScalarValue::Boolean(true);
assert!(b.as_boolean().unwrap());
}
#[test]
fn test_proxy_scalar_type_mismatch_unit() {
let s = ScalarValue::String("test".to_string());
assert!(s.as_integer().is_err());
}
}