use sea_orm::Condition;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JoinedColumnDef {
pub join_field: &'static str,
pub column_name: &'static str,
pub full_path: &'static str,
}
#[derive(Debug, Clone)]
pub struct JoinedFilter {
pub join_field: String,
pub column: String,
pub operator: FilterOperator,
pub value: serde_json::Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilterOperator {
Eq,
Neq,
Gt,
Gte,
Lt,
Lte,
Like,
In,
IsNull,
}
impl FilterOperator {
#[must_use]
pub fn from_suffix(suffix: &str) -> Option<Self> {
match suffix {
"_gte" => Some(Self::Gte),
"_lte" => Some(Self::Lte),
"_gt" => Some(Self::Gt),
"_lt" => Some(Self::Lt),
"_neq" => Some(Self::Neq),
"_like" => Some(Self::Like),
_ => None,
}
}
#[must_use]
pub fn suffix(&self) -> &'static str {
match self {
Self::Eq => "",
Self::Neq => "_neq",
Self::Gt => "_gt",
Self::Gte => "_gte",
Self::Lt => "_lt",
Self::Lte => "_lte",
Self::Like => "_like",
Self::In => "",
Self::IsNull => "",
}
}
}
#[derive(Debug)]
pub struct ParsedFilters {
pub main_condition: Condition,
pub joined_filters: Vec<JoinedFilter>,
pub has_joined_filters: bool,
}
impl Default for ParsedFilters {
fn default() -> Self {
Self {
main_condition: Condition::all(),
joined_filters: Vec::new(),
has_joined_filters: false,
}
}
}
#[derive(Debug, Clone)]
pub enum SortConfig<C> {
Column {
column: C,
direction: sea_orm::Order,
},
Joined {
join_field: String,
column: String,
direction: sea_orm::Order,
},
}
impl<C> SortConfig<C> {
pub fn is_joined(&self) -> bool {
matches!(self, Self::Joined { .. })
}
pub fn direction(&self) -> sea_orm::Order {
match self {
Self::Column { direction, .. } | Self::Joined { direction, .. } => direction.clone(),
}
}
}
#[must_use]
pub fn parse_dot_notation(field: &str) -> Option<(String, String, FilterOperator)> {
let dot_pos = field.find('.')?;
let join_field = &field[..dot_pos];
let rest = &field[dot_pos + 1..];
if join_field.is_empty() || rest.is_empty() {
return None;
}
for suffix in ["_gte", "_lte", "_gt", "_lt", "_neq", "_like"] {
if let Some(column) = rest.strip_suffix(suffix) {
let op = FilterOperator::from_suffix(suffix).unwrap_or(FilterOperator::Eq);
return Some((join_field.to_string(), column.to_string(), op));
}
}
Some((join_field.to_string(), rest.to_string(), FilterOperator::Eq))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_dot_notation_simple() {
let result = parse_dot_notation("vehicles.make");
assert_eq!(
result,
Some((
"vehicles".to_string(),
"make".to_string(),
FilterOperator::Eq
))
);
}
#[test]
fn test_parse_dot_notation_with_operator() {
let result = parse_dot_notation("vehicles.year_gte");
assert_eq!(
result,
Some((
"vehicles".to_string(),
"year".to_string(),
FilterOperator::Gte
))
);
let result = parse_dot_notation("vehicles.year_lte");
assert_eq!(
result,
Some((
"vehicles".to_string(),
"year".to_string(),
FilterOperator::Lte
))
);
}
#[test]
fn test_parse_dot_notation_no_dot() {
let result = parse_dot_notation("name");
assert_eq!(result, None);
}
#[test]
fn test_parse_dot_notation_empty_parts() {
assert_eq!(parse_dot_notation(".make"), None);
assert_eq!(parse_dot_notation("vehicles."), None);
assert_eq!(parse_dot_notation("."), None);
}
#[test]
fn test_joined_column_def() {
let def = JoinedColumnDef {
join_field: "vehicles",
column_name: "year",
full_path: "vehicles.year",
};
assert_eq!(def.join_field, "vehicles");
assert_eq!(def.full_path, "vehicles.year");
}
}