use vantage_expressions::{Expression, expr};
use crate::{identifier::Identifier, operation::Expressive};
#[derive(Debug, Clone)]
pub struct FieldProjectionField {
alias: String,
expression: Expression,
}
impl FieldProjectionField {
pub fn new(alias: impl Into<String>, expression: impl Into<Expression>) -> Self {
Self {
alias: alias.into(),
expression: expression.into(),
}
}
}
impl Expressive for FieldProjectionField {
fn expr(&self) -> Expression {
expr!(
"{}: {}",
Identifier::new(self.alias.clone()),
self.expression.expr()
)
}
}
impl From<FieldProjectionField> for Expression {
fn from(val: FieldProjectionField) -> Self {
val.expr()
}
}
#[derive(Debug, Clone)]
pub struct FieldProjection {
base: Option<Expression>,
fields: Vec<FieldProjectionField>,
}
impl FieldProjection {
pub fn new(base: impl Into<Expression>) -> Self {
Self {
base: Some(base.into()),
fields: Vec::new(),
}
}
pub fn with_field(mut self, field_name: impl Into<String>) -> Self {
self.add_field(field_name);
self
}
pub fn with_expression(
mut self,
expression: impl Into<Expression>,
alias: impl Into<String>,
) -> Self {
self.add_expression(expression, alias);
self
}
pub fn add_field(&mut self, field_name: impl Into<String>) {
let field_name = field_name.into();
self.fields.push(FieldProjectionField::new(
field_name.clone(),
expr!(field_name),
));
}
pub fn add_expression(&mut self, expression: impl Into<Expression>, alias: impl Into<String>) {
self.fields
.push(FieldProjectionField::new(alias, expression));
}
}
impl Expressive for FieldProjection {
fn expr(&self) -> Expression {
let field_expressions =
Expression::from_vec(self.fields.iter().map(|f| f.expr()).collect(), ", ");
let base = self.base.clone().unwrap();
if base.preview().is_empty() {
Expression::new("{{}}", vec![field_expressions.into()])
} else {
Expression::new("{}.{{}}", vec![base.into(), field_expressions.into()])
}
}
}
impl From<FieldProjection> for Expression {
fn from(val: FieldProjection) -> Self {
val.expr()
}
}
#[cfg(test)]
mod tests {
use super::*;
use vantage_expressions::expr;
#[test]
fn test_empty_projection() {
let projection = FieldProjection::new(expr!("lines[*]"));
assert_eq!(projection.expr().preview(), "lines[*].{}");
}
#[test]
fn test_single_field() {
let projection = FieldProjection::new(expr!("lines[*]")).with_field("quantity");
assert_eq!(projection.expr().preview(), "lines[*].{quantity: quantity}");
}
#[test]
fn test_multiple_fields() {
let projection = FieldProjection::new(expr!("lines[*]"))
.with_field("quantity")
.with_field("price")
.with_expression(expr!("product.name"), "product_name")
.with_expression(expr!("quantity * price"), "subtotal");
let expected = "lines[*].{quantity: quantity, price: price, product_name: product.name, subtotal: quantity * price}";
assert_eq!(projection.expr().preview(), expected);
}
#[test]
fn test_mutable_methods() {
let mut projection = FieldProjection::new(expr!("items[*]"));
projection.add_field("name");
projection.add_expression(expr!("count(*)"), "total");
let expected = "items[*].{name: name, total: count(*)}";
assert_eq!(projection.expr().preview(), expected);
}
#[test]
fn test_example_from_query07() {
let projection = FieldProjection::new(expr!("lines[*]"))
.with_field("quantity")
.with_field("price")
.with_expression(expr!("product.name"), "product_name")
.with_expression(expr!("quantity * price"), "subtotal");
let expected = "lines[*].{quantity: quantity, price: price, product_name: product.name, subtotal: quantity * price}";
assert_eq!(projection.expr().preview(), expected);
}
#[test]
fn test_empty_base() {
let projection = FieldProjection::new(expr!(""))
.with_field("quantity")
.with_field("price")
.with_expression(expr!("product.name"), "product_name");
let expected = "{quantity: quantity, price: price, product_name: product.name}";
assert_eq!(projection.expr().preview(), expected);
}
#[test]
fn test_empty_base_empty_fields() {
let projection = FieldProjection::new(expr!(""));
assert_eq!(projection.expr().preview(), "{}");
}
}