krabka_client_streams/runtime/iqv2/
result.rs1use std::collections::BTreeMap;
5
6use super::request::Position;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum FailureReason {
12 UnknownQueryType,
14 NotUpToBound,
16 NotPresent,
18 NotActive,
21 DoesNotExist,
23 StoreException,
26}
27
28pub enum QueryResult<R> {
30 Success {
31 result: R,
32 position: Position,
33 },
34 Failure {
35 reason: FailureReason,
36 message: String,
37 },
38}
39
40impl<R> QueryResult<R> {
41 #[must_use]
42 pub fn is_success(&self) -> bool {
43 matches!(self, QueryResult::Success { .. })
44 }
45 #[must_use]
46 pub fn result(&self) -> Option<&R> {
47 match self {
48 QueryResult::Success { result, .. } => Some(result),
49 QueryResult::Failure { .. } => None,
50 }
51 }
52 #[must_use]
53 pub fn into_result(self) -> Option<R> {
54 match self {
55 QueryResult::Success { result, .. } => Some(result),
56 QueryResult::Failure { .. } => None,
57 }
58 }
59 #[must_use]
60 pub fn position(&self) -> Option<&Position> {
61 match self {
62 QueryResult::Success { position, .. } => Some(position),
63 QueryResult::Failure { .. } => None,
64 }
65 }
66 #[must_use]
67 pub fn failure_reason(&self) -> Option<FailureReason> {
68 match self {
69 QueryResult::Failure { reason, .. } => Some(*reason),
70 QueryResult::Success { .. } => None,
71 }
72 }
73 #[must_use]
74 pub fn failure_message(&self) -> Option<&str> {
75 match self {
76 QueryResult::Failure { message, .. } => Some(message),
77 QueryResult::Success { .. } => None,
78 }
79 }
80}
81
82pub struct StateQueryResult<R> {
84 partition_results: BTreeMap<i32, QueryResult<R>>,
85}
86
87impl<R> StateQueryResult<R> {
88 #[must_use]
89 pub(crate) fn new(partition_results: BTreeMap<i32, QueryResult<R>>) -> Self {
90 Self { partition_results }
91 }
92 #[must_use]
93 pub fn partition_results(&self) -> &BTreeMap<i32, QueryResult<R>> {
94 &self.partition_results
95 }
96 #[must_use]
99 pub fn only_partition_result(&self) -> Option<&QueryResult<R>> {
100 if self.partition_results.len() == 1 {
101 self.partition_results.values().next()
102 } else {
103 None
104 }
105 }
106 #[must_use]
109 pub fn global_result(&self) -> Option<&QueryResult<R>> {
110 None
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn success_accessors_expose_result_and_position() {
120 let qr: QueryResult<i64> = QueryResult::Success {
121 result: 42,
122 position: Position::default(),
123 };
124 assert!(qr.is_success());
125 assert_eq!(qr.result(), Some(&42));
126 assert_eq!(qr.position(), Some(&Position::default()));
127 assert_eq!(qr.failure_reason(), None);
128 assert_eq!(qr.failure_message(), None);
129 assert_eq!(qr.into_result(), Some(42));
130 }
131
132 #[test]
133 fn failure_accessors_expose_reason_and_message() {
134 let qr: QueryResult<i64> = QueryResult::Failure {
135 reason: FailureReason::NotPresent,
136 message: "store not on this task".to_string(),
137 };
138 assert!(!qr.is_success());
139 assert_eq!(qr.result(), None);
140 assert_eq!(qr.position(), None);
141 assert_eq!(qr.failure_reason(), Some(FailureReason::NotPresent));
142 assert_eq!(qr.failure_message(), Some("store not on this task"));
143 assert_eq!(qr.into_result(), None);
144 }
145
146 #[test]
147 fn state_query_result_multi_partition_has_no_only_or_global() {
148 let mut map: BTreeMap<i32, QueryResult<Option<i64>>> = BTreeMap::new();
149 map.insert(
150 0,
151 QueryResult::Success {
152 result: Some(1),
153 position: Position::default(),
154 },
155 );
156 map.insert(
157 1,
158 QueryResult::Success {
159 result: Some(2),
160 position: Position::default(),
161 },
162 );
163 let sqr = StateQueryResult::new(map);
164 assert_eq!(sqr.partition_results().len(), 2);
165 assert!(sqr.only_partition_result().is_none());
166 assert!(sqr.global_result().is_none());
167 }
168
169 #[test]
170 fn state_query_result_single_partition_has_only() {
171 let mut map: BTreeMap<i32, QueryResult<Option<i64>>> = BTreeMap::new();
172 map.insert(
173 3,
174 QueryResult::Success {
175 result: Some(9),
176 position: Position::default(),
177 },
178 );
179 let sqr = StateQueryResult::new(map);
180 assert_eq!(sqr.partition_results().len(), 1);
181 let only = sqr.only_partition_result().expect("exactly one partition");
182 assert_eq!(only.result(), Some(&Some(9)));
183 }
184}