use rudb_common::{LogicalType, Value};
use rudb_vector::Vector;
use crate::regexp;
use crate::scalar;
use crate::shape::single;
#[derive(Debug)]
pub struct Recipe {
name: String,
hoisted: Hoisted,
}
#[derive(Debug)]
pub(crate) enum Hoisted {
Nothing,
Like(scalar::Like),
Regexp(Box<regexp::Call>),
}
impl Recipe {
#[must_use]
pub fn new(name: &str, literals: &[Option<Value>]) -> Self {
let hoisted = scalar::hoist(name, literals).unwrap_or(Hoisted::Nothing);
Self { name: name.to_owned(), hoisted }
}
#[must_use]
pub fn plain(name: &str) -> Self {
Self { name: name.to_owned(), hoisted: Hoisted::Nothing }
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn hoists(&self) -> bool {
!matches!(self.hoisted, Hoisted::Nothing)
}
pub(crate) fn hoisted(&self) -> &Hoisted {
&self.hoisted
}
}
#[derive(Debug)]
pub struct Held {
value: Value,
single: Vector,
}
impl Held {
#[must_use]
pub fn of(ty: &LogicalType, value: &Value) -> Option<Self> {
Some(Self { value: value.clone(), single: single(ty, value)? })
}
pub(crate) fn matches(&self, ty: &LogicalType, value: &Value) -> bool {
self.single.logical_type() == ty && self.value == *value
}
pub(crate) fn single(&self) -> &Vector {
&self.single
}
}
impl Hoisted {
pub(crate) fn like(&self) -> Option<&scalar::Like> {
match self {
Self::Like(like) => Some(like),
_ => None,
}
}
pub(crate) fn regexp(&self) -> Option<®exp::Call> {
match self {
Self::Regexp(call) => Some(call),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use rudb_common::Value;
use super::Recipe;
fn text(spelling: &str) -> Option<Value> {
Some(Value::Varchar(spelling.to_owned()))
}
#[test]
fn a_like_against_a_literal_pattern_is_compiled_here() {
let recipe = Recipe::new("~~", &[None, text("%google%")]);
assert!(recipe.hoisted().like().is_some());
assert_eq!(recipe.name(), "~~");
}
#[test]
fn a_pattern_that_is_not_a_literal_is_left_to_the_chunk() {
assert!(Recipe::new("~~", &[None, None]).hoisted().like().is_none());
}
#[test]
fn a_regular_expression_against_a_literal_pattern_is_compiled_here() {
let recipe = Recipe::new("regexp_matches", &[None, text("^a.*z$")]);
assert!(recipe.hoisted().regexp().is_some());
}
#[test]
fn a_pattern_that_does_not_compile_is_left_to_the_chunk() {
let recipe = Recipe::new("regexp_matches", &[None, text("a(")]);
assert!(recipe.hoisted().regexp().is_none());
}
#[test]
fn a_function_with_nothing_to_lift_lifts_nothing() {
let recipe = Recipe::new("upper", &[None]);
assert!(recipe.hoisted().like().is_none());
assert!(recipe.hoisted().regexp().is_none());
}
#[test]
fn a_plain_recipe_is_the_name_and_no_more() {
let recipe = Recipe::plain("~~");
assert_eq!(recipe.name(), "~~");
assert!(recipe.hoisted().like().is_none());
}
}