exocore_store/
ordering.rs1use std::cmp::Ordering;
2
3use exocore_protos::generated::exocore_store::{ordering_value, OrderingValue, Paging};
4
5use crate::mutation::OperationId;
6
7#[derive(Clone, Debug, PartialEq, Default)]
11pub struct OrderingValueWrapper {
12 pub value: OrderingValue,
13 pub reverse: bool,
14
15 pub ignore: bool,
18}
19
20impl PartialOrd for OrderingValueWrapper {
21 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
22 Some(self.cmp(other))
23 }
24}
25
26impl Ord for OrderingValueWrapper {
27 fn cmp(&self, other: &Self) -> Ordering {
28 if self.ignore && other.ignore {
30 return std::cmp::Ordering::Equal;
31 } else if self.ignore {
32 return std::cmp::Ordering::Less;
33 } else if other.ignore {
34 return std::cmp::Ordering::Greater;
35 }
36
37 let cmp = self
38 .value
39 .partial_cmp(&other.value)
40 .expect("expected value to be comparable");
41
42 if self.reverse {
44 cmp.reverse()
45 } else {
46 cmp
47 }
48 }
49}
50
51impl Eq for OrderingValueWrapper {}
52
53impl OrderingValueWrapper {
54 pub fn is_within_bound(&self, lower: &OrderingValue, higher: &OrderingValue) -> bool {
55 self.value.is_after(lower) && self.value.is_before(higher)
56 }
57
58 pub fn is_score(&self) -> bool {
59 matches!(
60 self.value.value.as_ref(),
61 Some(ordering_value::Value::Float(_score))
62 )
63 }
64
65 pub fn boost_score(&mut self, multiplier: f32) -> (f32, f32) {
66 if let Some(ordering_value::Value::Float(score)) = self.value.value.as_mut() {
67 let before = *score;
68 if !self.reverse {
69 *score *= multiplier;
70 } else {
71 *score /= multiplier;
72 }
73 (before, *score)
74 } else {
75 (0.0, 0.0)
76 }
77 }
78}
79
80pub trait OrderingValueExt {
82 fn partial_cmp(&self, other: &Self) -> Option<Ordering>;
83 fn is_after(&self, other: &Self) -> bool;
84 fn is_before(&self, other: &Self) -> bool;
85 fn is_within_page_bound(&self, page: &Paging) -> bool;
86}
87
88impl OrderingValueExt for OrderingValue {
89 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
90 use std::cmp::Ordering as O;
91
92 use ordering_value::Value as V;
93
94 if self.value == other.value {
95 return self.operation_id.partial_cmp(&other.operation_id);
96 }
97
98 match (self.value.as_ref(), other.value.as_ref()) {
99 (Some(V::Min(_)), _) => Some(O::Less),
100 (_, Some(V::Min(_))) => Some(O::Greater),
101 (Some(V::Max(_)), _) => Some(O::Greater),
102 (_, Some(V::Max(_))) => Some(O::Less),
103 (Some(V::Float(va)), Some(V::Float(vb))) => va.partial_cmp(vb),
104 (Some(V::Uint64(va)), Some(V::Uint64(vb))) => va.partial_cmp(vb),
105 (Some(V::Date(va)), Some(V::Date(vb))) => {
106 if va.seconds != vb.seconds {
107 va.seconds.partial_cmp(&vb.seconds)
108 } else {
109 va.nanos.partial_cmp(&vb.nanos)
110 }
111 }
112 _other => None,
113 }
114 }
115
116 fn is_after(&self, other: &Self) -> bool {
117 matches!(self.partial_cmp(other), Some(std::cmp::Ordering::Greater))
118 }
119
120 fn is_before(&self, other: &Self) -> bool {
121 matches!(self.partial_cmp(other), Some(std::cmp::Ordering::Less))
122 }
123
124 fn is_within_page_bound(&self, page: &Paging) -> bool {
125 if let Some(before) = page.before_ordering_value.as_ref() {
126 if !self.is_before(before) {
127 return false;
128 }
129 }
130
131 if let Some(after) = page.after_ordering_value.as_ref() {
132 if !self.is_after(after) {
133 return false;
134 }
135 }
136
137 true
138 }
139}
140
141pub fn value_from_u64(value: u64, operation_id: OperationId) -> OrderingValue {
142 OrderingValue {
143 value: Some(ordering_value::Value::Uint64(value)),
144 operation_id,
145 }
146}
147
148pub fn value_from_f32(value: f32, operation_id: OperationId) -> OrderingValue {
149 OrderingValue {
150 value: Some(ordering_value::Value::Float(value)),
151 operation_id,
152 }
153}
154
155pub fn value_max() -> OrderingValue {
156 OrderingValue {
157 value: Some(ordering_value::Value::Max(true)),
158 operation_id: 0,
159 }
160}
161
162pub fn value_min() -> OrderingValue {
163 OrderingValue {
164 value: Some(ordering_value::Value::Min(true)),
165 operation_id: 0,
166 }
167}