Skip to main content

solverforge_core/domain/
entity_ref.rs

1/* Entity reference types for dynamic entity access.
2
3These types enable the solver to work with entities at runtime without
4knowing their concrete types at compile time.
5*/
6
7use std::any::Any;
8use std::fmt::Debug;
9
10/* A reference to a planning entity with its index in the solution.
11
12This struct provides a way to identify and access entities during solving
13without needing to know the concrete entity type.
14*/
15#[derive(Debug, Clone)]
16pub struct EntityRef {
17    // Index of this entity in its collection.
18    pub index: usize,
19    // Name of the entity type.
20    pub type_name: &'static str,
21    // Name of the collection field in the solution.
22    pub collection_field: &'static str,
23}
24
25impl EntityRef {
26    pub fn new(index: usize, type_name: &'static str, collection_field: &'static str) -> Self {
27        Self {
28            index,
29            type_name,
30            collection_field,
31        }
32    }
33}
34
35/// Trait for extracting entities from a planning solution.
36///
37/// This trait is implemented by closures/functions that can extract
38/// entity references from a solution of a specific type.
39pub trait EntityExtractor: Send + Sync {
40    fn count(&self, solution: &dyn Any) -> Option<usize>;
41
42    fn get<'a>(&self, solution: &'a dyn Any, index: usize) -> Option<&'a dyn Any>;
43
44    fn get_mut<'a>(&self, solution: &'a mut dyn Any, index: usize) -> Option<&'a mut dyn Any>;
45
46    // Returns an iterator over entity references.
47    fn entity_refs(&self, solution: &dyn Any) -> Vec<EntityRef>;
48
49    // Clone this extractor.
50    fn clone_box(&self) -> Box<dyn EntityExtractor>;
51
52    /* Clones an entity as a boxed value for insertion into the constraint session.
53
54    This is used for incremental scoring where entities need to be inserted
55    into the BAVET session as owned, type-erased values.
56    */
57    fn clone_entity_boxed(
58        &self,
59        solution: &dyn Any,
60        index: usize,
61    ) -> Option<Box<dyn Any + Send + Sync>>;
62
63    fn entity_type_id(&self) -> std::any::TypeId;
64}
65
66impl Clone for Box<dyn EntityExtractor> {
67    fn clone(&self) -> Self {
68        self.clone_box()
69    }
70}
71
72/// A concrete entity extractor for a specific solution and entity type.
73///
74/// # Type Parameters
75/// * `S` - The solution type
76/// * `E` - The entity type
77pub struct TypedEntityExtractor<S, E> {
78    // Name of the entity type.
79    type_name: &'static str,
80    // Name of the collection field in the solution.
81    collection_field: &'static str,
82    // Function to get the entity collection from a solution.
83    get_collection: fn(&S) -> &Vec<E>,
84    // Function to get the mutable entity collection from a solution.
85    get_collection_mut: fn(&mut S) -> &mut Vec<E>,
86}
87
88impl<S, E> TypedEntityExtractor<S, E>
89where
90    S: 'static,
91    E: 'static,
92{
93    pub fn new(
94        type_name: &'static str,
95        collection_field: &'static str,
96        get_collection: fn(&S) -> &Vec<E>,
97        get_collection_mut: fn(&mut S) -> &mut Vec<E>,
98    ) -> Self {
99        Self {
100            type_name,
101            collection_field,
102            get_collection,
103            get_collection_mut,
104        }
105    }
106}
107
108impl<S, E> EntityExtractor for TypedEntityExtractor<S, E>
109where
110    S: Send + Sync + 'static,
111    E: Clone + Send + Sync + 'static,
112{
113    fn count(&self, solution: &dyn Any) -> Option<usize> {
114        let solution = solution.downcast_ref::<S>()?;
115        Some((self.get_collection)(solution).len())
116    }
117
118    fn get<'a>(&self, solution: &'a dyn Any, index: usize) -> Option<&'a dyn Any> {
119        let solution = solution.downcast_ref::<S>()?;
120        let collection = (self.get_collection)(solution);
121        collection.get(index).map(|e| e as &dyn Any)
122    }
123
124    fn get_mut<'a>(&self, solution: &'a mut dyn Any, index: usize) -> Option<&'a mut dyn Any> {
125        let solution = solution.downcast_mut::<S>()?;
126        let collection = (self.get_collection_mut)(solution);
127        collection.get_mut(index).map(|e| e as &mut dyn Any)
128    }
129
130    fn entity_refs(&self, solution: &dyn Any) -> Vec<EntityRef> {
131        let Some(solution) = solution.downcast_ref::<S>() else {
132            return Vec::new();
133        };
134        let collection = (self.get_collection)(solution);
135        (0..collection.len())
136            .map(|i| EntityRef::new(i, self.type_name, self.collection_field))
137            .collect()
138    }
139
140    fn clone_box(&self) -> Box<dyn EntityExtractor> {
141        Box::new(Self {
142            type_name: self.type_name,
143            collection_field: self.collection_field,
144            get_collection: self.get_collection,
145            get_collection_mut: self.get_collection_mut,
146        })
147    }
148
149    fn clone_entity_boxed(
150        &self,
151        solution: &dyn Any,
152        index: usize,
153    ) -> Option<Box<dyn Any + Send + Sync>> {
154        let solution = solution.downcast_ref::<S>()?;
155        let collection = (self.get_collection)(solution);
156        let entity = collection.get(index)?;
157        Some(Box::new(entity.clone()) as Box<dyn Any + Send + Sync>)
158    }
159
160    fn entity_type_id(&self) -> std::any::TypeId {
161        std::any::TypeId::of::<E>()
162    }
163}
164
165impl<S, E> Debug for TypedEntityExtractor<S, E> {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        f.debug_struct("TypedEntityExtractor")
168            .field("type_name", &self.type_name)
169            .field("collection_field", &self.collection_field)
170            .finish()
171    }
172}