use crate::proxy::{ProxyError, ProxyResult, ScalarValue};
use std::any::Any;
pub trait Reflectable {
fn get_relationship(&self, name: &str) -> Option<Box<dyn Any + 'static>>;
fn get_relationship_mut(&mut self, name: &str) -> Option<&mut dyn Any>;
fn get_attribute(&self, name: &str) -> Option<ScalarValue>;
fn set_attribute(&mut self, name: &str, value: ScalarValue) -> ProxyResult<()>;
fn get_relationship_attribute(
&self,
relationship: &str,
attribute: &str,
) -> ProxyResult<Option<ScalarValue>> {
let rel = self
.get_relationship(relationship)
.ok_or_else(|| ProxyError::RelationshipNotFound(relationship.to_string()))?;
let related = downcast_relationship::<Box<dyn Reflectable>>(rel)?;
Ok(related.get_attribute(attribute))
}
fn set_relationship_attribute(
&mut self,
relationship: &str,
attribute: &str,
value: ScalarValue,
) -> ProxyResult<()>;
fn has_relationship(&self, name: &str) -> bool {
self.get_relationship(name).is_some()
}
fn has_attribute(&self, name: &str) -> bool {
self.get_attribute(name).is_some()
}
fn as_any(&self) -> &dyn Any {
panic!("as_any() not implemented for this type");
}
}
pub trait ReflectableFactory: Send + Sync {
fn create_from_scalar(
&self,
attribute_name: &str,
value: ScalarValue,
) -> ProxyResult<Box<dyn Reflectable>>;
}
pub trait ProxyCollection {
type Item;
fn items(&self) -> Vec<&Self::Item>;
fn add(&mut self, item: Self::Item);
fn remove(&mut self, item: &Self::Item) -> bool;
fn contains(&self, item: &Self::Item) -> bool;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn clear(&mut self);
}
impl<T> ProxyCollection for Vec<T>
where
T: PartialEq,
{
type Item = T;
fn items(&self) -> Vec<&Self::Item> {
self.iter().collect()
}
fn add(&mut self, item: Self::Item) {
self.push(item);
}
fn remove(&mut self, item: &Self::Item) -> bool {
if let Some(pos) = self.iter().position(|x| x == item) {
self.remove(pos);
true
} else {
false
}
}
fn contains(&self, item: &Self::Item) -> bool {
self.iter().any(|x| x == item)
}
fn len(&self) -> usize {
Vec::len(self)
}
fn clear(&mut self) {
Vec::clear(self);
}
}
pub trait AttributeExtractor {
fn extract_attribute(&self, name: &str) -> ProxyResult<ScalarValue>;
}
pub fn downcast_relationship<T: 'static>(relationship: Box<dyn Any>) -> ProxyResult<Box<T>> {
relationship
.downcast::<T>()
.map_err(|_| ProxyError::TypeMismatch {
expected: std::any::type_name::<T>().to_string(),
actual: "unknown".to_string(),
})
}
pub fn extract_collection_values<T>(
collection: &[T],
attribute_extractor: impl Fn(&T) -> ProxyResult<ScalarValue>,
) -> ProxyResult<Vec<ScalarValue>> {
collection.iter().map(attribute_extractor).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, PartialEq)]
struct TestModel {
id: i64,
name: String,
}
impl Reflectable for TestModel {
fn get_relationship(&self, _name: &str) -> Option<Box<dyn Any + 'static>> {
None
}
fn get_relationship_mut(&mut self, _name: &str) -> Option<&mut dyn Any> {
None
}
fn get_attribute(&self, name: &str) -> Option<ScalarValue> {
match name {
"id" => Some(ScalarValue::Integer(self.id)),
"name" => Some(ScalarValue::String(self.name.clone())),
_ => None,
}
}
fn set_attribute(&mut self, name: &str, value: ScalarValue) -> ProxyResult<()> {
match name {
"id" => {
self.id = value.as_integer()?;
Ok(())
}
"name" => {
self.name = value.as_string()?;
Ok(())
}
_ => Err(ProxyError::AttributeNotFound(name.to_string())),
}
}
fn set_relationship_attribute(
&mut self,
relationship: &str,
_attribute: &str,
_value: ScalarValue,
) -> ProxyResult<()> {
Err(ProxyError::RelationshipNotFound(relationship.to_string()))
}
}
#[test]
fn test_reflectable_get_attribute() {
let model = TestModel {
id: 42,
name: "test".to_string(),
};
let id = model.get_attribute("id");
assert!(id.is_some());
assert_eq!(id.unwrap().as_integer().unwrap(), 42);
let name = model.get_attribute("name");
assert!(name.is_some());
assert_eq!(name.unwrap().as_string().unwrap(), "test");
}
#[test]
fn test_reflectable_set_attribute() {
let mut model = TestModel {
id: 42,
name: "test".to_string(),
};
model
.set_attribute("name", ScalarValue::String("new_name".to_string()))
.unwrap();
assert_eq!(model.name, "new_name");
model
.set_attribute("id", ScalarValue::Integer(100))
.unwrap();
assert_eq!(model.id, 100);
}
#[test]
fn test_reflectable_has_attribute() {
let model = TestModel {
id: 42,
name: "test".to_string(),
};
assert!(model.has_attribute("id"));
assert!(model.has_attribute("name"));
assert!(!model.has_attribute("nonexistent"));
}
#[test]
fn test_proxy_collection_vec() {
use super::ProxyCollection;
let mut vec: Vec<i32> = vec![1, 2, 3];
assert_eq!(ProxyCollection::len(&vec), 3);
assert!(ProxyCollection::contains(&vec, &2));
ProxyCollection::add(&mut vec, 4);
assert_eq!(ProxyCollection::len(&vec), 4);
assert!(ProxyCollection::remove(&mut vec, &2));
assert_eq!(ProxyCollection::len(&vec), 3);
assert!(!ProxyCollection::contains(&vec, &2));
ProxyCollection::clear(&mut vec);
assert!(ProxyCollection::is_empty(&vec));
}
#[test]
fn test_extract_collection_values() {
let models = vec![
TestModel {
id: 1,
name: "first".to_string(),
},
TestModel {
id: 2,
name: "second".to_string(),
},
];
let names = extract_collection_values(&models, |m| Ok(ScalarValue::String(m.name.clone())))
.unwrap();
assert_eq!(names.len(), 2);
assert_eq!(names[0].as_string().unwrap(), "first");
assert_eq!(names[1].as_string().unwrap(), "second");
}
}