1use serde::{Deserialize, Serialize};
2
3use crate::expr::Expr;
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6pub struct PropertyProjection {
7 pub source: String,
9 pub alias: String,
11}
12
13impl PropertyProjection {
14 pub fn new(name: impl Into<String>) -> Self {
16 let name = name.into();
17 Self {
18 source: name.clone(),
19 alias: name,
20 }
21 }
22
23 pub fn renamed(source: impl Into<String>, alias: impl Into<String>) -> Self {
25 Self {
26 source: source.into(),
27 alias: alias.into(),
28 }
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub struct ExprProjection {
35 pub alias: String,
37 pub expr: Expr,
39}
40
41impl ExprProjection {
42 pub fn new(alias: impl Into<String>, expr: Expr) -> Self {
44 Self {
45 alias: alias.into(),
46 expr,
47 }
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53#[serde(rename_all = "snake_case")]
54pub enum Projection {
55 Property(PropertyProjection),
57 Expr(ExprProjection),
59}
60
61impl Projection {
62 pub fn property(source: impl Into<String>, alias: impl Into<String>) -> Self {
64 Self::Property(PropertyProjection::renamed(source, alias))
65 }
66
67 pub fn from_endpoint(source: impl Into<String>, alias: impl Into<String>) -> Self {
69 Self::property(format!("$from.{}", source.into()), alias)
70 }
71
72 pub fn to_endpoint(source: impl Into<String>, alias: impl Into<String>) -> Self {
74 Self::property(format!("$to.{}", source.into()), alias)
75 }
76
77 pub fn expr(alias: impl Into<String>, expr: Expr) -> Self {
79 Self::Expr(ExprProjection::new(alias, expr))
80 }
81}
82
83impl From<PropertyProjection> for Projection {
84 fn from(value: PropertyProjection) -> Self {
85 Self::Property(value)
86 }
87}
88
89impl From<ExprProjection> for Projection {
90 fn from(value: ExprProjection) -> Self {
91 Self::Expr(value)
92 }
93}
94
95#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(rename_all = "snake_case")]
108pub enum BindingTarget {
109 Current,
111 Binding(String),
113}
114
115impl BindingTarget {
116 pub fn current() -> Self {
118 Self::Current
119 }
120
121 pub fn binding(name: impl Into<String>) -> Self {
123 Self::Binding(non_empty_string(name, "binding name"))
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
139pub struct BindingValueRef {
140 pub target: BindingTarget,
142 pub source: String,
144}
145
146impl BindingValueRef {
147 pub fn new(target: BindingTarget, source: impl Into<String>) -> Self {
149 Self {
150 target,
151 source: non_empty_string(source, "binding projection source"),
152 }
153 }
154
155 pub fn current(source: impl Into<String>) -> Self {
157 Self::new(BindingTarget::Current, source)
158 }
159
160 pub fn binding(name: impl Into<String>, source: impl Into<String>) -> Self {
162 Self::new(BindingTarget::binding(name), source)
163 }
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
184#[serde(rename_all = "snake_case")]
185pub enum BindingProjection {
186 Property {
188 target: BindingTarget,
190 source: String,
192 alias: String,
194 },
195 Coalesce {
197 refs: Vec<BindingValueRef>,
199 alias: String,
201 },
202}
203
204impl BindingProjection {
205 pub fn property(
207 target: BindingTarget,
208 source: impl Into<String>,
209 alias: impl Into<String>,
210 ) -> Self {
211 Self::Property {
212 target,
213 source: non_empty_string(source, "binding projection source"),
214 alias: non_empty_string(alias, "binding projection alias"),
215 }
216 }
217
218 pub fn current(source: impl Into<String>, alias: impl Into<String>) -> Self {
220 Self::property(BindingTarget::Current, source, alias)
221 }
222
223 pub fn binding(
225 name: impl Into<String>,
226 source: impl Into<String>,
227 alias: impl Into<String>,
228 ) -> Self {
229 Self::property(BindingTarget::binding(name), source, alias)
230 }
231
232 pub fn coalesce(refs: Vec<BindingValueRef>, alias: impl Into<String>) -> Self {
234 assert!(!refs.is_empty(), "binding coalesce refs must not be empty");
235 Self::Coalesce {
236 refs,
237 alias: non_empty_string(alias, "binding projection alias"),
238 }
239 }
240}
241
242pub(crate) fn validate_binding_name(name: impl Into<String>) -> String {
243 non_empty_string(name, "binding name")
244}
245
246pub(crate) fn validate_binding_projections(
247 projections: Vec<BindingProjection>,
248) -> Vec<BindingProjection> {
249 assert!(
250 !projections.is_empty(),
251 "binding projections must not be empty"
252 );
253 projections
254}
255
256fn non_empty_string(value: impl Into<String>, field: &str) -> String {
257 let value = value.into();
258 assert!(!value.is_empty(), "{field} must not be empty");
259 value
260}