use bson::Bson;
use crate::vm::operators::{OpRegistry, OperatorExpr, VmOperator};
use crate::{Result, Error};
pub(crate) struct AbsOperator {
inner: OperatorExpr,
}
impl AbsOperator {
fn bson_abs(input: Bson) -> Bson {
match input {
Bson::Int32(v) => {
let result = v.abs();
Bson::Int32(result)
}
Bson::Int64(v) => {
let result = v.abs();
Bson::Int64(result)
}
_ => input,
}
}
pub(crate) fn compile(paths: &mut Vec<String>, registry: OpRegistry, v: &Bson) -> Result<Box<dyn VmOperator>> {
let inner = match v {
Bson::Document(doc) => {
let op = registry.compile_doc(paths, doc)?;
OperatorExpr::Expr(op)
}
Bson::Null => {
OperatorExpr::Constant(Bson::Null)
}
Bson::Int32(v) => {
let result = v.abs();
OperatorExpr::Constant(Bson::Int32(result))
}
Bson::Int64(v) => {
let result = v.abs();
OperatorExpr::Constant(Bson::Int64(result))
}
Bson::String(field_name) => {
if let Some(stripped_field_name) = field_name.strip_prefix("$") {
OperatorExpr::Alias(stripped_field_name.to_string())
} else {
return Err(Error::UnknownAggregationOperation("$abs".to_string()));
}
}
_ => {
return Err(Error::UnknownAggregationOperation("$abs".to_string()));
}
};
Ok(Box::new(AbsOperator {
inner,
}))
}
}
impl VmOperator for AbsOperator {
fn initial_value(&self) -> Bson {
match self.inner {
OperatorExpr::Constant(ref v) => v.clone(),
OperatorExpr::Expr(ref op) =>
Self::bson_abs(op.initial_value()),
OperatorExpr::Alias(_) => Bson::Null,
}
}
fn next(&self, input: &Bson) -> Bson {
match self.inner {
OperatorExpr::Constant(ref v) => v.clone(),
OperatorExpr::Expr(ref op) =>
Self::bson_abs(op.next(input)),
OperatorExpr::Alias(ref field_name) => {
let unwrap = match input {
Bson::Document(doc) => doc.get(field_name).cloned(),
_ => None,
}.unwrap_or(Bson::Null);
Self::bson_abs(unwrap)
}
}
}
fn complete(&self) -> Bson {
match self.inner {
OperatorExpr::Constant(ref v) => v.clone(),
OperatorExpr::Expr(ref op) =>
Self::bson_abs(op.complete()),
OperatorExpr::Alias(_) => Bson::Null,
}
}
}