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}