sift_query_core/protocol/
rpc.rs1use crate::{
3 NodeId, PagingMode, ParamError, ParamNames, ParamParser, PredicateArena, PredicateNode,
4 Projection, QueryBuilder, QueryInput, QueryPolicy, RawParams, SearchEntity, SortOrder,
5 UserQuery,
6};
7
8impl<E: SearchEntity> UserQuery<E> {
9 pub fn to_rpc(&self, policy: &QueryPolicy) -> Result<RawParams, ParamError> {
10 self.to_rpc_with_names(policy, policy.names())
11 }
12
13 pub fn to_rpc_with_names(
15 &self,
16 policy: &QueryPolicy,
17 names: &ParamNames,
18 ) -> Result<RawParams, ParamError> {
19 self.into_user_query(policy)?;
20 let target = policy
21 .clone()
22 .with_param_names(names.clone())
23 .map_err(|error| ParamError::ConflictingParameters(error.to_string()))?;
24 let mut entries = Vec::new();
25 if let Some(root) = self.predicate().root() {
26 let expression = encode_node::<E>(self.predicate(), root, policy, &mut entries, None)?;
27 entries.push((names.group_expr.clone(), expression));
28 }
29 if let Projection::Fields(fields) = self.projection() {
30 entries.push((
31 names.only_select.clone(),
32 fields
33 .iter()
34 .map(|id| E::META.field(*id).expect("validated field").public_name)
35 .collect::<Vec<_>>()
36 .join(","),
37 ));
38 }
39 for length in 0..=self.order().len() {
42 let mut order = if length == 0 {
43 E::META
44 .default_order
45 .iter()
46 .copied()
47 .map(Into::into)
48 .collect::<Vec<_>>()
49 } else {
50 self.order()[..length].to_vec()
51 };
52 for stable in E::META.stable_order {
53 if !order.iter().any(|item| item.field == stable.field) {
54 order.push((*stable).into());
55 }
56 }
57 if order == self.order() {
58 if length > 0 {
59 entries.push((
60 names.order_by.clone(),
61 self.order()[..length]
62 .iter()
63 .map(|order| {
64 format!(
65 "{}:{}",
66 E::META
67 .field(order.field)
68 .expect("validated order")
69 .public_name,
70 if order.direction == SortOrder::Asc {
71 "asc"
72 } else {
73 "desc"
74 }
75 )
76 })
77 .collect::<Vec<_>>()
78 .join(","),
79 ));
80 }
81 break;
82 }
83 }
84 let paging = self.paging();
85 if self.is_forced_empty() {
86 entries.push((
87 names.size.clone(),
88 (u64::from(
89 policy
90 .limits()
91 .max_page_size
92 .min(E::META.limits.max_page_size),
93 ) + 1)
94 .to_string(),
95 ));
96 } else {
97 if paging.explicit_size || paging.size != 15 {
98 entries.push((names.size.clone(), paging.size.to_string()));
99 }
100 match &paging.mode {
101 PagingMode::Page { page } if paging.explicit_page => {
102 entries.push((names.page.clone(), page.to_string()));
103 }
104 PagingMode::Offset => {
105 entries.push((names.offset.clone(), paging.offset.to_string()));
106 }
107 PagingMode::Cursor(cursor) => {
108 let token = match cursor {
109 None => String::new(),
110 Some(cursor) => policy
111 .cursor_codec()
112 .ok_or_else(|| invalid("cursor codec missing"))?
113 .encode::<E>(self.order(), cursor.values())?,
114 };
115 entries.push((names.cursor.clone(), token));
116 }
117 PagingMode::Page { .. } => {}
118 }
119 }
120 let raw = RawParams::from_entries(entries, target.limits())?;
121 let decoded = ParamParser::new(&target)?.parse::<E>(&raw)?;
124 if signature(self.predicate()) != signature(decoded.predicate())
125 || self.order() != decoded.order()
126 || self.projection() != decoded.projection()
127 || self.paging() != decoded.paging()
128 || self.is_forced_empty() != decoded.is_forced_empty()
129 {
130 return Err(invalid("RPC normalization changes query semantics"));
131 }
132 Ok(raw)
133 }
134}
135
136impl<E: SearchEntity> QueryBuilder<'_, E> {
137 pub fn build_for_rpc(self) -> Result<RawParams, ParamError> {
138 let policy = self.policy;
139 self.build()?.to_rpc(policy)
140 }
141 pub fn build_for_rpc_with_names(self, names: &ParamNames) -> Result<RawParams, ParamError> {
142 let policy = self.policy;
143 self.build()?.to_rpc_with_names(policy, names)
144 }
145}
146
147fn encode_node<E: SearchEntity>(
148 arena: &PredicateArena,
149 id: NodeId,
150 policy: &QueryPolicy,
151 entries: &mut Vec<(String, String)>,
152 parent_is_and: Option<bool>,
153) -> Result<String, ParamError> {
154 match arena.node(id).expect("validated predicate") {
155 PredicateNode::Condition(condition) => {
156 let group = format!("q{}", id.0);
157 let field = E::META.field(condition.field).expect("validated field");
158 let key = format!("{group}.{}", field.public_name);
159 entries.push((
160 format!("{key}-op"),
161 condition.operator.short_name().to_owned(),
162 ));
163 if condition.ignore_case {
164 entries.push((format!("{key}-ic"), "true".to_owned()));
165 }
166 for value in &condition.values {
167 let input_field = crate::FieldMeta {
168 value_type: condition.operator.input_type(field.value_type),
169 ..*field
170 };
171 let text = if let Some(codec) = policy.codec_for::<E>(field) {
172 let text = codec.format_input(&input_field, value).map_err(|reason| {
173 ParamError::InvalidValue {
174 field: field.public_name.to_owned(),
175 reason,
176 }
177 })?;
178 if codec.parse(&input_field, &text).ok().as_ref() != Some(value) {
179 return Err(invalid("codec has no lossless RPC representation"));
180 }
181 text
182 } else {
183 value.to_external_text()
184 };
185 entries.push((key.clone(), text));
186 }
187 Ok(group)
188 }
189 PredicateNode::And(children) | PredicateNode::Or(children) => {
190 let and = matches!(arena.node(id), Some(PredicateNode::And(_)));
191 let separator = if and { "&" } else { "|" };
192 let expressions = children
193 .iter()
194 .map(|child| encode_node::<E>(arena, *child, policy, entries, Some(and)))
195 .collect::<Result<Vec<_>, _>>()?;
196 let expression = expressions.join(separator);
197 Ok(if !and && parent_is_and == Some(true) {
200 format!("({expression})")
201 } else {
202 expression
203 })
204 }
205 }
206}
207
208#[derive(PartialEq)]
209enum Expression {
210 Condition(crate::Condition),
211 Junction(bool, Vec<Self>),
212}
213fn signature(arena: &PredicateArena) -> Option<Expression> {
214 fn node(arena: &PredicateArena, id: NodeId) -> Expression {
215 match arena.node(id).expect("validated predicate") {
216 PredicateNode::Condition(condition) => Expression::Condition(condition.clone()),
217 PredicateNode::And(children) | PredicateNode::Or(children) => {
218 let and = matches!(arena.node(id), Some(PredicateNode::And(_)));
219 let mut output = Vec::new();
220 for child in children {
221 match node(arena, *child) {
222 Expression::Junction(kind, nested) if kind == and => output.extend(nested),
223 child => output.push(child),
224 }
225 }
226 if output.len() == 1 {
227 output.remove(0)
228 } else {
229 Expression::Junction(and, output)
230 }
231 }
232 }
233 }
234 arena.root().map(|root| node(arena, root))
235}
236fn invalid(reason: &str) -> ParamError {
237 ParamError::InvalidValue {
238 field: "rpc".to_owned(),
239 reason: reason.to_owned(),
240 }
241}