Skip to main content

couchbase_core/analyticsx/
query_options.rs

1/*
2 *
3 *  * Copyright (c) 2025 Couchbase, Inc.
4 *  *
5 *  * Licensed under the Apache License, Version 2.0 (the "License");
6 *  * you may not use this file except in compliance with the License.
7 *  * You may obtain a copy of the License at
8 *  *
9 *  *    http://www.apache.org/licenses/LICENSE-2.0
10 *  *
11 *  * Unless required by applicable law or agreed to in writing, software
12 *  * distributed under the License is distributed on an "AS IS" BASIS,
13 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  * See the License for the specific language governing permissions and
15 *  * limitations under the License.
16 *
17 */
18
19use std::collections::HashMap;
20use std::time::Duration;
21
22use serde::ser::{SerializeMap, SerializeSeq};
23use serde::{Serialize, Serializer};
24use serde_json::Value;
25
26use crate::helpers;
27use crate::httpx::request::OnBehalfOfInfo;
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
30#[serde(rename_all = "snake_case")]
31#[non_exhaustive]
32pub enum ScanConsistency {
33    NotBounded,
34    RequestPlus,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
38#[serde(rename_all = "UPPERCASE")]
39#[non_exhaustive]
40pub enum Format {
41    Json,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
45#[serde(rename_all = "UPPERCASE")]
46#[non_exhaustive]
47pub enum PlanFormat {
48    Json,
49    String,
50}
51
52#[derive(Debug, Clone, Default)]
53#[non_exhaustive]
54pub struct QueryOptions {
55    pub(crate) args: Option<Vec<Value>>,
56    pub(crate) client_context_id: Option<String>,
57    pub(crate) format: Option<Format>,
58    pub(crate) pretty: Option<bool>,
59    pub(crate) query_context: Option<String>,
60    pub(crate) read_only: Option<bool>,
61    pub(crate) scan_consistency: Option<ScanConsistency>,
62    pub(crate) scan_wait: Option<Duration>,
63    pub(crate) statement: Option<String>,
64    pub(crate) timeout: Option<Duration>,
65    pub(crate) named_args: Option<HashMap<String, Value>>,
66    pub(crate) raw: Option<HashMap<String, Value>>,
67
68    pub(crate) plan_format: Option<PlanFormat>,
69    pub(crate) logical_plan: Option<bool>,
70    pub(crate) optimized_logical_plan: Option<bool>,
71    pub(crate) expression_tree: Option<bool>,
72    pub(crate) rewritten_expression_tree: Option<bool>,
73    pub(crate) job: Option<bool>,
74    pub(crate) max_warnings: Option<i32>,
75
76    pub(crate) on_behalf_of: Option<OnBehalfOfInfo>,
77}
78
79impl QueryOptions {
80    pub fn new() -> Self {
81        Default::default()
82    }
83
84    pub fn args(mut self, args: impl Into<Option<Vec<Value>>>) -> Self {
85        self.args = args.into();
86        self
87    }
88
89    pub fn client_context_id(mut self, client_context_id: impl Into<Option<String>>) -> Self {
90        self.client_context_id = client_context_id.into();
91        self
92    }
93
94    pub fn pretty(mut self, pretty: impl Into<Option<bool>>) -> Self {
95        self.pretty = pretty.into();
96        self
97    }
98
99    pub fn query_context(mut self, query_context: impl Into<Option<String>>) -> Self {
100        self.query_context = query_context.into();
101        self
102    }
103
104    pub fn read_only(mut self, read_only: impl Into<Option<bool>>) -> Self {
105        self.read_only = read_only.into();
106        self
107    }
108
109    pub fn scan_consistency(
110        mut self,
111        scan_consistency: impl Into<Option<ScanConsistency>>,
112    ) -> Self {
113        self.scan_consistency = scan_consistency.into();
114        self
115    }
116
117    pub fn scan_wait(mut self, scan_wait: impl Into<Option<Duration>>) -> Self {
118        self.scan_wait = scan_wait.into();
119        self
120    }
121
122    pub fn statement(mut self, statement: impl Into<Option<String>>) -> Self {
123        self.statement = statement.into();
124        self
125    }
126
127    pub fn timeout(mut self, timeout: impl Into<Option<Duration>>) -> Self {
128        self.timeout = timeout.into();
129        self
130    }
131
132    pub fn named_args(mut self, named_args: impl Into<Option<HashMap<String, Value>>>) -> Self {
133        self.named_args = named_args.into();
134        self
135    }
136
137    pub fn raw(mut self, raw: impl Into<Option<HashMap<String, Value>>>) -> Self {
138        self.raw = raw.into();
139        self
140    }
141
142    pub fn plan_format(mut self, plan_format: impl Into<Option<PlanFormat>>) -> Self {
143        self.plan_format = plan_format.into();
144        self
145    }
146
147    pub fn logical_plan(mut self, logical_plan: impl Into<Option<bool>>) -> Self {
148        self.logical_plan = logical_plan.into();
149        self
150    }
151
152    pub fn optimized_logical_plan(
153        mut self,
154        optimized_logical_plan: impl Into<Option<bool>>,
155    ) -> Self {
156        self.optimized_logical_plan = optimized_logical_plan.into();
157        self
158    }
159
160    pub fn expression_tree(mut self, expression_tree: impl Into<Option<bool>>) -> Self {
161        self.expression_tree = expression_tree.into();
162        self
163    }
164
165    pub fn rewritten_expression_tree(
166        mut self,
167        rewritten_expression_tree: impl Into<Option<bool>>,
168    ) -> Self {
169        self.rewritten_expression_tree = rewritten_expression_tree.into();
170        self
171    }
172
173    pub fn job(mut self, job: impl Into<Option<bool>>) -> Self {
174        self.job = job.into();
175        self
176    }
177
178    pub fn max_warnings(mut self, max_warnings: impl Into<Option<i32>>) -> Self {
179        self.max_warnings = max_warnings.into();
180        self
181    }
182
183    pub fn on_behalf_of(mut self, on_behalf_of: impl Into<Option<OnBehalfOfInfo>>) -> Self {
184        self.on_behalf_of = on_behalf_of.into();
185        self
186    }
187}
188
189impl Serialize for QueryOptions {
190    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
191    where
192        S: Serializer,
193    {
194        use helpers::durations;
195        let mut map = serializer.serialize_map(None)?;
196
197        macro_rules! serialize_if_not_none {
198            ($field:expr, $name:expr) => {
199                if !$field.is_none() {
200                    map.serialize_entry($name, &$field)?;
201                }
202            };
203        }
204
205        macro_rules! serialize_duration_if_not_none {
206            ($field:expr, $name:expr) => {
207                if let Some(f) = $field {
208                    map.serialize_entry($name, &durations::duration_to_golang_string(&f))?;
209                }
210            };
211        }
212
213        serialize_if_not_none!(self.args, "args");
214        serialize_if_not_none!(self.client_context_id, "client_context_id");
215        serialize_if_not_none!(self.format, "format");
216        serialize_if_not_none!(self.pretty, "pretty");
217        serialize_if_not_none!(self.query_context, "query_context");
218        serialize_if_not_none!(self.read_only, "readonly");
219        serialize_if_not_none!(self.scan_consistency, "scan_consistency");
220        serialize_duration_if_not_none!(self.scan_wait, "scan_wait");
221        serialize_if_not_none!(self.statement, "statement");
222        serialize_duration_if_not_none!(self.timeout, "timeout");
223
224        serialize_if_not_none!(self.plan_format, "plan_format");
225        serialize_if_not_none!(self.logical_plan, "logical_plan");
226        serialize_if_not_none!(self.optimized_logical_plan, "optimized_logical_plan");
227        serialize_if_not_none!(self.expression_tree, "expression_tree");
228        serialize_if_not_none!(self.rewritten_expression_tree, "rewritten_expression_tree");
229        serialize_if_not_none!(self.job, "job");
230        serialize_if_not_none!(self.max_warnings, "max_warnings");
231
232        if let Some(args) = &self.named_args {
233            // Prefix each named_arg with "$" if not already prefixed.
234            for (key, value) in args {
235                let key = if key.starts_with('$') {
236                    key
237                } else {
238                    &format!("${key}")
239                };
240                map.serialize_entry(key, value)?;
241            }
242        }
243
244        if let Some(raw) = &self.raw {
245            // Move raw fields to the top level.
246            for (key, value) in raw {
247                map.serialize_entry(key, value)?;
248            }
249        }
250
251        map.end()
252    }
253}
254
255#[derive(Debug, Clone, Default)]
256#[non_exhaustive]
257pub struct PingOptions<'a> {
258    pub(crate) on_behalf_of: Option<&'a OnBehalfOfInfo>,
259}
260
261impl<'a> PingOptions<'a> {
262    pub fn new() -> Self {
263        Default::default()
264    }
265
266    pub fn on_behalf_of(mut self, on_behalf_of: impl Into<Option<&'a OnBehalfOfInfo>>) -> Self {
267        self.on_behalf_of = on_behalf_of.into();
268        self
269    }
270}
271
272#[derive(Debug, Clone, Default)]
273#[non_exhaustive]
274pub struct GetPendingMutationsOptions<'a> {
275    pub(crate) on_behalf_of: Option<&'a OnBehalfOfInfo>,
276}
277
278impl<'a> GetPendingMutationsOptions<'a> {
279    pub fn new() -> Self {
280        Default::default()
281    }
282
283    pub fn on_behalf_of(mut self, on_behalf_of: impl Into<Option<&'a OnBehalfOfInfo>>) -> Self {
284        self.on_behalf_of = on_behalf_of.into();
285        self
286    }
287}