use std::borrow::Cow;
use super::Operator;
use crate::{
Bool, Likeable, LowerCompatible,
expression::Expression,
lower::{Instructions, LowerCtx},
};
#[derive(Debug)]
pub struct Like<L, R> {
pub case_sensitive: bool,
pub negated: bool,
pub left: L,
pub right: R,
}
pub fn prepare_sqlite_glob<'a>(value: &'a str) -> Cow<'a, str> {
let mut first_special = None;
for (i, ch) in value.char_indices() {
match ch {
'*' | '?' | '%' | '_' => {
first_special = Some(i);
break;
}
_ => {}
}
}
let Some(idx) = first_special else {
return Cow::Borrowed(value);
};
let mut out = String::with_capacity(value.len());
out.push_str(&value[..idx]);
for ch in value[idx..].chars() {
match ch {
'*' => {
out.push('[');
out.push('*');
out.push(']');
}
'?' => {
out.push('[');
out.push('?');
out.push(']');
}
'%' => out.push('*'),
'_' => out.push('?'),
_ => out.push(ch),
}
}
Cow::Owned(out)
}
#[qraft_expression_macro::as_expression]
impl<L, R, T> Expression for Like<L, R>
where
T: Likeable,
L: Expression<Type = T>,
R: LowerCompatible<T>,
{
type Type = Bool;
fn lower(&self, ctx: &mut LowerCtx) -> usize {
let lhs = self.left.lower(ctx);
let rhs = self.right.lower_compatible(ctx);
ctx.instrs.push_binary(
Operator::Like {
sensitive: self.case_sensitive,
negated: self.negated,
},
lhs,
rhs,
);
lhs + rhs + 1
}
}
impl<L, R> Like<L, R> {
pub fn case_sensitive(mut self) -> Self {
self.case_sensitive = true;
self
}
}
pub trait LikeExt<T: Likeable>: Sized + Expression {
fn like<E>(self, other: E) -> Like<Self, E>
where
E: LowerCompatible<T>,
{
Like {
left: self,
right: other,
case_sensitive: false,
negated: false,
}
}
fn not_like<E>(self, other: E) -> Like<Self, E>
where
E: LowerCompatible<T>,
{
Like {
left: self,
right: other,
case_sensitive: false,
negated: true,
}
}
}
impl<T: Likeable, V> LikeExt<T> for V where V: Expression<Type = T> {}