use serde::{Deserialize, Serialize};
use crate::expr::Expr;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PropertyProjection {
pub source: String,
pub alias: String,
}
impl PropertyProjection {
pub fn new(name: impl Into<String>) -> Self {
let name = name.into();
Self {
source: name.clone(),
alias: name,
}
}
pub fn renamed(source: impl Into<String>, alias: impl Into<String>) -> Self {
Self {
source: source.into(),
alias: alias.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExprProjection {
pub alias: String,
pub expr: Expr,
}
impl ExprProjection {
pub fn new(alias: impl Into<String>, expr: Expr) -> Self {
Self {
alias: alias.into(),
expr,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Projection {
Property(PropertyProjection),
Expr(ExprProjection),
}
impl Projection {
pub fn property(source: impl Into<String>, alias: impl Into<String>) -> Self {
Self::Property(PropertyProjection::renamed(source, alias))
}
pub fn from_endpoint(source: impl Into<String>, alias: impl Into<String>) -> Self {
Self::property(format!("$from.{}", source.into()), alias)
}
pub fn to_endpoint(source: impl Into<String>, alias: impl Into<String>) -> Self {
Self::property(format!("$to.{}", source.into()), alias)
}
pub fn expr(alias: impl Into<String>, expr: Expr) -> Self {
Self::Expr(ExprProjection::new(alias, expr))
}
}
impl From<PropertyProjection> for Projection {
fn from(value: PropertyProjection) -> Self {
Self::Property(value)
}
}
impl From<ExprProjection> for Projection {
fn from(value: ExprProjection) -> Self {
Self::Expr(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BindingTarget {
Current,
Binding(String),
}
impl BindingTarget {
pub fn current() -> Self {
Self::Current
}
pub fn binding(name: impl Into<String>) -> Self {
Self::Binding(non_empty_string(name, "binding name"))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BindingValueRef {
pub target: BindingTarget,
pub source: String,
}
impl BindingValueRef {
pub fn new(target: BindingTarget, source: impl Into<String>) -> Self {
Self {
target,
source: non_empty_string(source, "binding projection source"),
}
}
pub fn current(source: impl Into<String>) -> Self {
Self::new(BindingTarget::Current, source)
}
pub fn binding(name: impl Into<String>, source: impl Into<String>) -> Self {
Self::new(BindingTarget::binding(name), source)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BindingProjection {
Property {
target: BindingTarget,
source: String,
alias: String,
},
Coalesce {
refs: Vec<BindingValueRef>,
alias: String,
},
}
impl BindingProjection {
pub fn property(
target: BindingTarget,
source: impl Into<String>,
alias: impl Into<String>,
) -> Self {
Self::Property {
target,
source: non_empty_string(source, "binding projection source"),
alias: non_empty_string(alias, "binding projection alias"),
}
}
pub fn current(source: impl Into<String>, alias: impl Into<String>) -> Self {
Self::property(BindingTarget::Current, source, alias)
}
pub fn binding(
name: impl Into<String>,
source: impl Into<String>,
alias: impl Into<String>,
) -> Self {
Self::property(BindingTarget::binding(name), source, alias)
}
pub fn coalesce(refs: Vec<BindingValueRef>, alias: impl Into<String>) -> Self {
assert!(!refs.is_empty(), "binding coalesce refs must not be empty");
Self::Coalesce {
refs,
alias: non_empty_string(alias, "binding projection alias"),
}
}
}
pub(crate) fn validate_binding_name(name: impl Into<String>) -> String {
non_empty_string(name, "binding name")
}
pub(crate) fn validate_binding_projections(
projections: Vec<BindingProjection>,
) -> Vec<BindingProjection> {
assert!(
!projections.is_empty(),
"binding projections must not be empty"
);
projections
}
fn non_empty_string(value: impl Into<String>, field: &str) -> String {
let value = value.into();
assert!(!value.is_empty(), "{field} must not be empty");
value
}