Skip to main content

solverforge_scoring/stream/
factory.rs

1/* Constraint factory for creating typed constraint streams.
2
3The factory is the entry point for the fluent constraint API.
4*/
5
6use std::marker::PhantomData;
7
8use solverforge_core::score::Score;
9
10use super::collection_extract::{tracked, ChangeSource, CollectionExtract, TrackedExtract};
11use super::filter::TrueFilter;
12use super::UniConstraintStream;
13
14/* Factory for creating constraint streams.
15
16`ConstraintFactory` is parameterized by the solution type `S` and score type `Sc`.
17It serves as the entry point for defining constraints using the fluent API.
18
19# Example
20
21```
22use solverforge_scoring::stream::ConstraintFactory;
23use solverforge_scoring::api::constraint_set::IncrementalConstraint;
24use solverforge_core::score::SoftScore;
25
26#[derive(Clone)]
27struct Solution {
28values: Vec<Option<i32>>,
29}
30
31let factory = ConstraintFactory::<Solution, SoftScore>::new();
32
33let constraint = factory
34.for_each(|s: &Solution| &s.values)
35.filter(|v: &Option<i32>| v.is_none())
36.penalize(SoftScore::of(1))
37.named("Unassigned");
38
39let solution = Solution { values: vec![Some(1), None, None] };
40assert_eq!(constraint.evaluate(&solution), SoftScore::of(-2));
41```
42*/
43pub struct ConstraintFactory<S, Sc: Score> {
44    _phantom: PhantomData<(fn() -> S, fn() -> Sc)>,
45}
46
47impl<S, Sc> ConstraintFactory<S, Sc>
48where
49    S: Send + Sync + 'static,
50    Sc: Score + 'static,
51{
52    // Creates a new constraint factory.
53    pub fn new() -> Self {
54        Self {
55            _phantom: PhantomData,
56        }
57    }
58
59    /* Creates a zero-erasure uni-constraint stream over entities extracted from the solution.
60
61    The extractor function receives a reference to the solution and returns
62    a slice of entities to iterate over. The extractor type is preserved
63    as a concrete generic for full zero-erasure.
64    */
65    pub fn for_each<A, E>(self, extractor: E) -> UniConstraintStream<S, A, E, TrueFilter, Sc>
66    where
67        A: Clone + Send + Sync + 'static,
68        E: CollectionExtract<S, Item = A>,
69    {
70        UniConstraintStream::new(extractor)
71    }
72
73    pub fn for_each_tracked<A, E>(
74        self,
75        extractor: E,
76        change_source: ChangeSource,
77    ) -> UniConstraintStream<S, A, TrackedExtract<E>, TrueFilter, Sc>
78    where
79        A: Clone + Send + Sync + 'static,
80        E: CollectionExtract<S, Item = A>,
81    {
82        UniConstraintStream::new(tracked(extractor, change_source))
83    }
84}
85
86impl<S, Sc> Default for ConstraintFactory<S, Sc>
87where
88    S: Send + Sync + 'static,
89    Sc: Score + 'static,
90{
91    fn default() -> Self {
92        Self::new()
93    }
94}
95
96impl<S, Sc: Score> Clone for ConstraintFactory<S, Sc> {
97    fn clone(&self) -> Self {
98        Self {
99            _phantom: PhantomData,
100        }
101    }
102}
103
104impl<S, Sc: Score> std::fmt::Debug for ConstraintFactory<S, Sc> {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        f.debug_struct("ConstraintFactory").finish()
107    }
108}