solverforge_core/constraints/
named_expr.rs1use std::sync::atomic::{AtomicU64, Ordering};
23
24use crate::constraints::WasmFunction;
25use crate::wasm::Expression;
26
27static EXPR_COUNTER: AtomicU64 = AtomicU64::new(0);
29
30#[derive(Debug, Clone, PartialEq)]
38pub struct NamedExpression {
39 name: String,
40 expression: Expression,
41}
42
43impl NamedExpression {
44 pub fn new(expression: Expression) -> Self {
56 let counter = EXPR_COUNTER.fetch_add(1, Ordering::SeqCst);
57 Self {
58 name: format!("expr_{}", counter),
59 expression,
60 }
61 }
62
63 pub fn with_name(name: impl Into<String>, expression: Expression) -> Self {
81 Self {
82 name: name.into(),
83 expression,
84 }
85 }
86
87 pub fn name(&self) -> &str {
89 &self.name
90 }
91
92 pub fn expression(&self) -> &Expression {
94 &self.expression
95 }
96
97 pub fn into_expression(self) -> Expression {
99 self.expression
100 }
101
102 pub fn into_parts(self) -> (String, Expression) {
104 (self.name, self.expression)
105 }
106}
107
108impl From<NamedExpression> for WasmFunction {
109 fn from(named: NamedExpression) -> WasmFunction {
110 WasmFunction::with_expression(named.name, named.expression)
111 }
112}
113
114impl From<&NamedExpression> for WasmFunction {
115 fn from(named: &NamedExpression) -> WasmFunction {
116 WasmFunction::with_expression(&named.name, named.expression.clone())
117 }
118}
119
120pub trait IntoNamedExpression {
122 fn named(self) -> NamedExpression;
124
125 fn named_as(self, name: impl Into<String>) -> NamedExpression;
127}
128
129impl IntoNamedExpression for Expression {
130 fn named(self) -> NamedExpression {
131 NamedExpression::new(self)
132 }
133
134 fn named_as(self, name: impl Into<String>) -> NamedExpression {
135 NamedExpression::with_name(name, self)
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::wasm::{Expr, FieldAccessExt};
143
144 #[test]
145 fn test_named_expression_new() {
146 let expr = Expr::is_not_null(Expr::param(0));
147 let named = NamedExpression::new(expr.clone());
148
149 assert!(named.name().starts_with("expr_"));
150 assert_eq!(named.expression(), &expr);
151 }
152
153 #[test]
154 fn test_named_expression_with_name() {
155 let expr = Expr::is_not_null(Expr::param(0));
156 let named = NamedExpression::with_name("my_predicate", expr.clone());
157
158 assert_eq!(named.name(), "my_predicate");
159 assert_eq!(named.expression(), &expr);
160 }
161
162 #[test]
163 fn test_unique_names() {
164 let expr1 = NamedExpression::new(Expr::bool(true));
165 let expr2 = NamedExpression::new(Expr::bool(false));
166
167 assert_ne!(expr1.name(), expr2.name());
168 }
169
170 #[test]
171 fn test_into_wasm_function() {
172 let named = NamedExpression::with_name("test_fn", Expr::bool(true));
173 let wasm_fn: WasmFunction = named.into();
174
175 assert_eq!(wasm_fn.name(), "test_fn");
176 }
177
178 #[test]
179 fn test_into_parts() {
180 let expr = Expr::int(42);
181 let named = NamedExpression::with_name("answer", expr.clone());
182 let (name, expression) = named.into_parts();
183
184 assert_eq!(name, "answer");
185 assert_eq!(expression, expr);
186 }
187
188 #[test]
189 fn test_extension_trait() {
190 let expr = Expr::is_not_null(Expr::param(0).get("Lesson", "room"));
191 let named = expr.clone().named();
192
193 assert!(named.name().starts_with("expr_"));
194 assert_eq!(named.expression(), &expr);
195 }
196
197 #[test]
198 fn test_extension_trait_named_as() {
199 let expr = Expr::is_not_null(Expr::param(0).get("Lesson", "room"));
200 let named = expr.clone().named_as("has_room");
201
202 assert_eq!(named.name(), "has_room");
203 assert_eq!(named.expression(), &expr);
204 }
205
206 #[test]
207 fn test_complex_expression() {
208 let has_room = Expr::is_not_null(Expr::param(0).get("Lesson", "room"));
210 let has_timeslot = Expr::is_not_null(Expr::param(0).get("Lesson", "timeslot"));
211 let both_assigned = Expr::and(has_room, has_timeslot);
212
213 let named = both_assigned.named_as("lesson_fully_assigned");
214
215 assert_eq!(named.name(), "lesson_fully_assigned");
216 match named.expression() {
217 Expression::And { .. } => {}
218 _ => panic!("Expected And expression"),
219 }
220 }
221}