use crate::exec::field_path::FieldPath;
use crate::exec::operators::SortDirection;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SortProperty {
pub path: FieldPath,
pub direction: SortDirection,
pub collate: bool,
pub numeric: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum OutputOrdering {
#[default]
Unordered,
Sorted(Vec<SortProperty>),
}
impl OutputOrdering {
pub fn satisfies(&self, required: &[SortProperty]) -> bool {
if required.is_empty() {
return true;
}
match self {
OutputOrdering::Unordered => false,
OutputOrdering::Sorted(provided) => {
if provided.len() < required.len() {
return false;
}
provided.iter().zip(required.iter()).all(|(p, r)| p == r)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn prop(name: &str, dir: SortDirection) -> SortProperty {
SortProperty {
path: FieldPath::field(name),
direction: dir,
collate: false,
numeric: false,
}
}
#[test]
fn test_empty_requirement_always_satisfied() {
assert!(OutputOrdering::Unordered.satisfies(&[]));
assert!(OutputOrdering::Sorted(vec![prop("a", SortDirection::Asc)]).satisfies(&[]));
}
#[test]
fn test_unordered_never_satisfies_nonempty() {
let req = vec![prop("a", SortDirection::Asc)];
assert!(!OutputOrdering::Unordered.satisfies(&req));
}
#[test]
fn test_exact_match() {
let ordering = OutputOrdering::Sorted(vec![
prop("a", SortDirection::Asc),
prop("b", SortDirection::Desc),
]);
let req = vec![prop("a", SortDirection::Asc), prop("b", SortDirection::Desc)];
assert!(ordering.satisfies(&req));
}
#[test]
fn test_superset_satisfies_prefix() {
let ordering = OutputOrdering::Sorted(vec![
prop("a", SortDirection::Asc),
prop("b", SortDirection::Asc),
prop("c", SortDirection::Asc),
]);
let req = vec![prop("a", SortDirection::Asc), prop("b", SortDirection::Asc)];
assert!(ordering.satisfies(&req));
}
#[test]
fn test_subset_does_not_satisfy() {
let ordering = OutputOrdering::Sorted(vec![prop("a", SortDirection::Asc)]);
let req = vec![prop("a", SortDirection::Asc), prop("b", SortDirection::Asc)];
assert!(!ordering.satisfies(&req));
}
#[test]
fn test_direction_mismatch() {
let ordering = OutputOrdering::Sorted(vec![prop("a", SortDirection::Asc)]);
let req = vec![prop("a", SortDirection::Desc)];
assert!(!ordering.satisfies(&req));
}
#[test]
fn test_path_mismatch() {
let ordering = OutputOrdering::Sorted(vec![prop("a", SortDirection::Asc)]);
let req = vec![prop("b", SortDirection::Asc)];
assert!(!ordering.satisfies(&req));
}
#[test]
fn test_collate_mismatch() {
let ordering = OutputOrdering::Sorted(vec![prop("a", SortDirection::Asc)]);
let req = vec![SortProperty {
path: FieldPath::field("a"),
direction: SortDirection::Asc,
collate: true,
numeric: false,
}];
assert!(!ordering.satisfies(&req));
}
#[test]
fn test_numeric_mismatch() {
let ordering = OutputOrdering::Sorted(vec![prop("a", SortDirection::Asc)]);
let req = vec![SortProperty {
path: FieldPath::field("a"),
direction: SortDirection::Asc,
collate: false,
numeric: true,
}];
assert!(!ordering.satisfies(&req));
}
}