Skip to main content

solverforge_scoring/stream/
factory.rs

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