Skip to main content

icydb_core/db/query/expr/
order.rs

1//! Module: query::expr::order
2//! Responsibility: typed fluent ORDER BY expression DTOs and lowering.
3//! Does not own: planner validation or execution-time order evaluation.
4//! Boundary: converts fluent order inputs into planner-owned order terms.
5
6use crate::db::query::{
7    builder::{
8        AggregateExpr, FieldRef, NumericProjectionExpr, RoundProjectionExpr, TextProjectionExpr,
9    },
10    plan::{
11        OrderDirection, OrderTerm as PlannedOrderTerm,
12        expr::{Expr, FieldId},
13    },
14};
15
16///
17/// OrderExpr
18///
19/// Typed fluent ORDER BY expression wrapper.
20/// This exists so fluent code can construct planner-owned ORDER BY
21/// semantics directly at the query boundary.
22///
23
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub struct OrderExpr {
26    expr: Expr,
27}
28
29impl OrderExpr {
30    /// Build one direct field ORDER BY expression.
31    #[must_use]
32    pub fn field(field: impl Into<String>) -> Self {
33        let field = field.into();
34
35        Self {
36            expr: Expr::Field(FieldId::new(field)),
37        }
38    }
39
40    // Freeze one typed fluent order expression onto the planner-owned
41    // semantic expression now that labels are derived only at explain/hash
42    // edges instead of being stored in fluent order shells.
43    const fn new(expr: Expr) -> Self {
44        Self { expr }
45    }
46
47    // Lower one typed fluent order expression into the planner-owned order
48    // contract now that ordering is expression-based end to end.
49    pub(in crate::db) fn lower(&self, direction: OrderDirection) -> PlannedOrderTerm {
50        PlannedOrderTerm::new(self.expr.clone(), direction)
51    }
52}
53
54impl From<&str> for OrderExpr {
55    fn from(value: &str) -> Self {
56        Self::field(value)
57    }
58}
59
60impl From<String> for OrderExpr {
61    fn from(value: String) -> Self {
62        Self::field(value)
63    }
64}
65
66impl From<FieldRef> for OrderExpr {
67    fn from(value: FieldRef) -> Self {
68        Self::field(value.as_str())
69    }
70}
71
72impl From<TextProjectionExpr> for OrderExpr {
73    fn from(value: TextProjectionExpr) -> Self {
74        Self::new(value.expr().clone())
75    }
76}
77
78impl From<NumericProjectionExpr> for OrderExpr {
79    fn from(value: NumericProjectionExpr) -> Self {
80        Self::new(value.expr().clone())
81    }
82}
83
84impl From<RoundProjectionExpr> for OrderExpr {
85    fn from(value: RoundProjectionExpr) -> Self {
86        Self::new(value.expr().clone())
87    }
88}
89
90impl From<AggregateExpr> for OrderExpr {
91    fn from(value: AggregateExpr) -> Self {
92        Self::new(Expr::Aggregate(value))
93    }
94}
95
96///
97/// OrderTerm
98///
99/// Typed fluent ORDER BY term.
100/// Carries one typed ORDER BY expression plus direction so fluent builders can
101/// express deterministic ordering directly at the query boundary.
102///
103
104#[derive(Clone, Debug, Eq, PartialEq)]
105pub struct OrderTerm {
106    expr: OrderExpr,
107    direction: OrderDirection,
108}
109
110impl OrderTerm {
111    /// Build one ascending ORDER BY term from one typed expression.
112    #[must_use]
113    pub fn asc(expr: impl Into<OrderExpr>) -> Self {
114        Self {
115            expr: expr.into(),
116            direction: OrderDirection::Asc,
117        }
118    }
119
120    /// Build one descending ORDER BY term from one typed expression.
121    #[must_use]
122    pub fn desc(expr: impl Into<OrderExpr>) -> Self {
123        Self {
124            expr: expr.into(),
125            direction: OrderDirection::Desc,
126        }
127    }
128
129    // Lower one typed fluent order term directly into the planner-owned
130    // `OrderTerm` contract.
131    pub(in crate::db) fn lower(&self) -> PlannedOrderTerm {
132        self.expr.lower(self.direction)
133    }
134}
135
136/// Build one typed direct-field ORDER BY expression.
137#[must_use]
138pub fn field(field: impl Into<String>) -> OrderExpr {
139    OrderExpr::field(field)
140}
141
142/// Build one ascending typed ORDER BY term.
143#[must_use]
144pub fn asc(expr: impl Into<OrderExpr>) -> OrderTerm {
145    OrderTerm::asc(expr)
146}
147
148/// Build one descending typed ORDER BY term.
149#[must_use]
150pub fn desc(expr: impl Into<OrderExpr>) -> OrderTerm {
151    OrderTerm::desc(expr)
152}