pub struct AssociationProxy<S, A, T> {
association_getter: Box<dyn Fn(&S) -> &A>,
attribute_getter: Box<dyn Fn(&A) -> &T>,
}
impl<S, A, T> AssociationProxy<S, A, T> {
pub fn new<F1, F2>(association_getter: F1, attribute_getter: F2) -> Self
where
F1: Fn(&S) -> &A + 'static,
F2: Fn(&A) -> &T + 'static,
{
Self {
association_getter: Box::new(association_getter),
attribute_getter: Box::new(attribute_getter),
}
}
pub fn get<'a>(&self, source: &'a S) -> &'a T
where
A: 'a,
{
let associated = (self.association_getter)(source);
(self.attribute_getter)(associated)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
#[derive(Clone)]
struct Address {
city: String,
country: String,
}
#[allow(dead_code)]
#[derive(Clone)]
struct User {
id: i64,
address: Address,
}
#[test]
fn test_association_proxy_basic() {
let address = Address {
city: "Tokyo".to_string(),
country: "Japan".to_string(),
};
let user = User { id: 1, address };
let city_proxy = AssociationProxy::new(|u: &User| &u.address, |a: &Address| &a.city);
assert_eq!(city_proxy.get(&user), "Tokyo");
}
#[test]
fn test_association_proxy_multiple_attributes() {
let address = Address {
city: "Paris".to_string(),
country: "France".to_string(),
};
let user = User { id: 1, address };
let city_proxy = AssociationProxy::new(|u: &User| &u.address, |a: &Address| &a.city);
let country_proxy = AssociationProxy::new(|u: &User| &u.address, |a: &Address| &a.country);
assert_eq!(city_proxy.get(&user), "Paris");
assert_eq!(country_proxy.get(&user), "France");
}
#[test]
fn test_association_proxy_full_object() {
let address = Address {
city: "Berlin".to_string(),
country: "Germany".to_string(),
};
let user = User { id: 1, address };
let address_proxy = AssociationProxy::new(|u: &User| &u.address, |a: &Address| a);
let addr = address_proxy.get(&user);
assert_eq!(addr.city, "Berlin");
assert_eq!(addr.country, "Germany");
}
}