1pub mod error;
2
3use error::{Error, Result};
4use indexmap::IndexMap;
5use std::str::FromStr;
6use url::form_urlencoded;
7
8pub const QUESTION: char = '?';
9pub const AMPERSAND: char = '&';
10pub const EQUAL: char = '=';
11pub const COLON: char = ':';
12pub const COMMA: char = ',';
13pub const PERCENT: char = '%';
14
15pub fn url_decode(input: &str) -> String {
17 if input.contains(PERCENT) {
19 let query_str = format!("key={}", input);
21 form_urlencoded::parse(query_str.as_bytes())
22 .next()
23 .map(|(_, v)| v.to_string())
24 .unwrap_or_else(|| input.to_string())
25 } else {
26 input.to_string()
27 }
28}
29
30pub fn url_encode(input: &str) -> String {
32 form_urlencoded::byte_serialize(input.as_bytes()).collect()
33}
34
35pub fn parse_parameter(s: &str) -> Result<(Similarity, Vec<String>)> {
42 let trimmed = s.trim();
43 if trimmed.is_empty() {
44 return Err(Error::InvalidParameter(s.into()));
45 }
46
47 let parts: Vec<&str> = trimmed.split(COLON).collect();
48 if parts.len() != 2 {
49 return Err(Error::InvalidParameter(s.into()));
50 }
51
52 let similarity_str = parts[0].trim();
53 let values_str = parts[1].trim();
54
55 if similarity_str.is_empty() {
56 return Err(Error::InvalidParameter(s.into()));
57 }
58
59 let values: Vec<String> = if values_str.is_empty() {
60 vec![]
61 } else {
62 values_str
63 .split(COMMA)
64 .map(|v| url_decode(v.trim()))
65 .filter(|v| !v.is_empty())
66 .collect()
67 };
68
69 let similarity = Similarity::from_str(similarity_str)?;
70 Ok((similarity, values))
71}
72
73pub fn parse_sort_field(s: &str) -> Result<(String, SortOrder)> {
79 let trimmed = s.trim();
80 if trimmed.is_empty() {
81 return Err(Error::InvalidSortField(s.into()));
82 }
83
84 let parts: Vec<&str> = trimmed.split(COLON).collect();
85 if parts.len() != 2 {
86 return Err(Error::InvalidSortField(s.into()));
87 }
88
89 let name = url_decode(parts[0].trim());
90 let order_str = parts[1].trim();
91
92 if name.is_empty() || order_str.is_empty() {
93 return Err(Error::InvalidSortField(s.into()));
94 }
95
96 let order = SortOrder::from_str(order_str)?;
97 Ok((name, order))
98}
99
100#[derive(Clone, Debug, PartialEq)]
101pub enum SortOrder {
102 Ascending,
103 Descending,
104}
105
106impl SortOrder {
107 pub const ASCENDING: &str = "asc";
108 pub const DESCENDING: &str = "desc";
109}
110
111impl Default for SortOrder {
112 fn default() -> Self {
113 Self::Ascending
114 }
115}
116
117impl FromStr for SortOrder {
118 type Err = Error;
119 fn from_str(s: &str) -> Result<Self> {
120 match s {
121 SortOrder::ASCENDING => Ok(SortOrder::Ascending),
122 SortOrder::DESCENDING => Ok(SortOrder::Descending),
123 val => Err(Error::InvalidSortOrder(val.into())),
124 }
125 }
126}
127
128impl ToString for SortOrder {
129 fn to_string(&self) -> String {
130 match self {
131 Self::Ascending => SortOrder::ASCENDING.to_string(),
132 Self::Descending => SortOrder::DESCENDING.to_string(),
133 }
134 }
135}
136
137#[derive(Clone, Debug, PartialEq)]
138pub struct SortFields(pub IndexMap<String, SortOrder>);
139
140impl SortFields {
141 pub fn new() -> Self {
142 Self(IndexMap::new())
143 }
144
145 pub fn inner(&self) -> &IndexMap<String, SortOrder> {
146 &self.0
147 }
148
149 pub fn inner_mut(&mut self) -> &mut IndexMap<String, SortOrder> {
150 &mut self.0
151 }
152
153 pub fn ascending(&mut self, name: String) -> &mut Self {
154 self.0.insert(name, SortOrder::Ascending);
155 self
156 }
157
158 pub fn descending(&mut self, name: String) -> &mut Self {
159 self.0.insert(name, SortOrder::Descending);
160 self
161 }
162
163 pub fn keep(&self, keys: Vec<String>) -> Self {
164 let mut result = Self::new();
165 for key in keys {
166 if let Some(value) = self.0.get(&key) {
167 result.0.insert(key, value.clone());
168 }
169 }
170 result
171 }
172
173 pub fn remove(&self, keys: Vec<String>) -> Self {
174 let mut result = self.clone();
175 for key in keys {
176 result.0.shift_remove(&key);
177 }
178 result
179 }
180}
181
182impl Default for SortFields {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188impl FromStr for SortFields {
189 type Err = Error;
190
191 fn from_str(s: &str) -> Result<Self> {
194 let trimmed = s.trim();
195 if trimmed.is_empty() {
196 return Ok(SortFields::new());
197 }
198
199 let str_fields: Vec<&str> = trimmed.split(COMMA).collect();
200 let mut sort_fields: Self = SortFields(IndexMap::new());
201
202 for str_field in str_fields {
203 let trimmed_field = str_field.trim();
204 if trimmed_field.is_empty() {
205 continue;
206 }
207
208 let (name, order) = parse_sort_field(trimmed_field)?;
209 sort_fields.0.insert(name, order);
210 }
211
212 Ok(sort_fields)
213 }
214}
215
216#[derive(Clone, Debug, PartialEq)]
217pub enum Similarity {
218 Equals,
219 Contains,
220 StartsWith,
221 EndsWith,
222
223 Between,
224 Lesser,
225 LesserOrEqual,
226 Greater,
227 GreaterOrEqual,
228}
229
230impl Similarity {
231 pub const EQUALS: &str = "equals";
232 pub const CONTAINS: &str = "contains";
233 pub const STARTS_WITH: &str = "starts-with";
234 pub const ENDS_WITH: &str = "ends-with";
235
236 pub const BETWEEN: &str = "between";
237 pub const LESSER: &str = "lesser";
238 pub const LESSER_OR_EQUAL: &str = "lesser-or-equal";
239 pub const GREATER: &str = "greater";
240 pub const GREATER_OR_EQUAL: &str = "greater-or-equal";
241}
242
243impl Default for Similarity {
244 fn default() -> Self {
245 Self::Equals
246 }
247}
248
249impl FromStr for Similarity {
250 type Err = Error;
251 fn from_str(s: &str) -> Result<Self> {
252 match s {
253 Similarity::EQUALS => Ok(Similarity::Equals),
254 Similarity::CONTAINS => Ok(Similarity::Contains),
255 Similarity::STARTS_WITH => Ok(Similarity::StartsWith),
256 Similarity::ENDS_WITH => Ok(Similarity::EndsWith),
257
258 Similarity::BETWEEN => Ok(Similarity::Between),
259 Similarity::LESSER => Ok(Similarity::Lesser),
260 Similarity::LESSER_OR_EQUAL => Ok(Similarity::LesserOrEqual),
261 Similarity::GREATER => Ok(Similarity::Greater),
262 Similarity::GREATER_OR_EQUAL => Ok(Similarity::GreaterOrEqual),
263
264 val => Err(Error::InvalidSimilarity(val.into())),
265 }
266 }
267}
268
269impl ToString for Similarity {
270 fn to_string(&self) -> String {
271 match self {
272 Self::Equals => Self::EQUALS.to_string(),
273 Self::Contains => Self::CONTAINS.to_string(),
274 Self::StartsWith => Self::STARTS_WITH.to_string(),
275 Self::EndsWith => Self::ENDS_WITH.to_string(),
276
277 Self::Between => Self::BETWEEN.to_string(),
278 Self::Lesser => Self::LESSER.to_string(),
279 Self::LesserOrEqual => Self::LESSER_OR_EQUAL.to_string(),
280 Self::Greater => Self::GREATER.to_string(),
281 Self::GreaterOrEqual => Self::GREATER_OR_EQUAL.to_string(),
282 }
283 }
284}
285
286pub type Parameter = (Similarity, Vec<String>);
287pub trait ParameterGet {
288 fn similarity(&self) -> &Similarity;
289 fn values(&self) -> &Vec<String>;
290}
291
292impl ParameterGet for Parameter {
293 fn similarity(&self) -> &Similarity {
294 &self.0
295 }
296
297 fn values(&self) -> &Vec<String> {
298 &self.1
299 }
300}
301
302#[derive(Clone, Debug, PartialEq)]
303pub struct Parameters(pub IndexMap<String, Parameter>);
304
305impl Parameters {
306 pub const ORDER: &str = "order";
307 pub const LIMIT: &str = "limit";
308 pub const OFFSET: &str = "offset";
309
310 pub const EXCLUDE: [&str; 3] = [Parameters::ORDER, Parameters::LIMIT, Parameters::OFFSET];
311
312 pub const DEFAULT_LIMIT: usize = 50;
313 pub const DEFAULT_OFFSET: usize = 0;
314
315 pub fn new() -> Self {
316 Self(IndexMap::new())
317 }
318
319 pub fn inner(&self) -> &IndexMap<String, Parameter> {
320 &self.0
321 }
322
323 pub fn inner_mut(&mut self) -> &mut IndexMap<String, Parameter> {
324 &mut self.0
325 }
326
327 pub fn equals(&mut self, key: String, values: Vec<String>) -> &mut Self {
328 self.0.insert(key, (Similarity::Equals, values));
329 self
330 }
331
332 pub fn contains(&mut self, key: String, values: Vec<String>) -> &mut Self {
333 self.0.insert(key, (Similarity::Contains, values));
334 self
335 }
336
337 pub fn starts_with(&mut self, key: String, values: Vec<String>) -> &mut Self {
338 self.0.insert(key, (Similarity::StartsWith, values));
339 self
340 }
341
342 pub fn ends_with(&mut self, key: String, values: Vec<String>) -> &mut Self {
343 self.0.insert(key, (Similarity::EndsWith, values));
344 self
345 }
346
347 pub fn between(&mut self, key: String, values: Vec<String>) -> &mut Self {
348 self.0.insert(key, (Similarity::Between, values));
349 self
350 }
351
352 pub fn lesser(&mut self, key: String, values: Vec<String>) -> &mut Self {
353 self.0.insert(key, (Similarity::Lesser, values));
354 self
355 }
356
357 pub fn lesser_or_equal(&mut self, key: String, values: Vec<String>) -> &mut Self {
358 self.0.insert(key, (Similarity::LesserOrEqual, values));
359 self
360 }
361
362 pub fn greater(&mut self, key: String, values: Vec<String>) -> &mut Self {
363 self.0.insert(key, (Similarity::Greater, values));
364 self
365 }
366
367 pub fn greater_or_equal(&mut self, key: String, values: Vec<String>) -> &mut Self {
368 self.0.insert(key, (Similarity::GreaterOrEqual, values));
369 self
370 }
371
372 pub fn keep(&self, keys: Vec<String>) -> Self {
373 let mut result = Self::new();
374 for key in keys {
375 if let Some(value) = self.0.get(&key) {
376 result.0.insert(key, value.clone());
377 }
378 }
379 result
380 }
381
382 pub fn remove(&self, keys: Vec<String>) -> Self {
383 let mut result = self.clone();
384 for key in keys {
385 result.0.shift_remove(&key);
386 }
387 result
388 }
389}
390
391impl Default for Parameters {
392 fn default() -> Self {
393 Self::new()
394 }
395}
396
397impl FromStr for Parameters {
398 type Err = Error;
399
400 fn from_str(s: &str) -> Result<Self> {
403 let trimmed = s.trim();
404 if trimmed.is_empty() {
405 return Ok(Parameters::new());
406 }
407
408 let str_parameters: Vec<&str> = trimmed.split(AMPERSAND).collect();
409 let mut parameters: Self = Parameters(IndexMap::new());
410
411 for str_param in str_parameters {
412 let trimmed_param = str_param.trim();
413 if trimmed_param.is_empty() {
414 continue;
415 }
416
417 let mut parts = trimmed_param.splitn(2, EQUAL);
418 let (key, value) = match (parts.next(), parts.next()) {
419 (Some(k), Some(v)) => (k, v),
420 _ => return Err(Error::InvalidParameter(trimmed_param.into())),
421 };
422
423 let trimmed_key = key.trim();
424 if trimmed_key.is_empty() || Parameters::EXCLUDE.contains(&trimmed_key) {
425 continue;
426 }
427
428 let (similarity, values) = parse_parameter(value)?;
429 if values.is_empty() {
431 continue;
432 }
433
434 parameters
435 .0
436 .insert(trimmed_key.to_string(), (similarity, values));
437 }
438
439 Ok(parameters)
440 }
441}
442
443#[derive(Clone, Debug, PartialEq)]
444pub enum SqlValue {
445 Null,
447 Integer(i64),
449 Real(f64),
451 Text(String),
453 Blob(Vec<u8>),
455}
456
457#[derive(Clone, Debug, PartialEq)]
458pub struct Query {
459 pub parameters: Parameters,
460 pub sort_fields: SortFields,
461 pub limit: usize,
462 pub offset: usize,
463}
464
465impl Query {
466 pub fn new() -> Self {
467 Self {
468 parameters: Parameters::new(),
469 sort_fields: SortFields::new(),
470 limit: Parameters::DEFAULT_LIMIT,
471 offset: Parameters::DEFAULT_OFFSET,
472 }
473 }
474
475 pub fn init(
476 parameters: Parameters,
477 sort_fields: SortFields,
478 limit: usize,
479 offset: usize,
480 ) -> Self {
481 Self {
482 parameters,
483 sort_fields,
484 limit,
485 offset,
486 }
487 }
488
489 pub fn to_http(&self) -> String {
490 let mut params = self
491 .parameters
492 .0
493 .iter()
494 .filter(|(_, (_, values))| values.len() > 0)
495 .map(|(key, (similarity, values))| {
496 let similarity_str = similarity.to_string();
497 let values_str = values
498 .iter()
499 .map(|v| url_encode(v))
500 .collect::<Vec<String>>()
501 .join(&format!("{COMMA}"));
502 format!("{key}{EQUAL}{similarity_str}{COLON}{values_str}",)
503 })
504 .collect::<Vec<String>>()
505 .join("&");
506
507 let order = self
508 .sort_fields
509 .0
510 .iter()
511 .filter(|(name, _)| name.len() > 0)
512 .map(|(name, order)| format!("{name}{COLON}{}", order.to_string()))
513 .collect::<Vec<String>>()
514 .join(&format!("{COMMA}"));
515
516 if params.len() > 0 {
517 params.push_str(&format!("{AMPERSAND}"));
518 }
519
520 if order.len() > 0 {
521 params.push_str(&format!("{}{EQUAL}{}", Parameters::ORDER, order));
522 params.push_str(&format!("{AMPERSAND}"));
523 }
524
525 format!(
526 "{params}{}{EQUAL}{}{AMPERSAND}{}{EQUAL}{}",
527 Parameters::LIMIT,
528 self.limit,
529 Parameters::OFFSET,
530 self.offset,
531 )
532 }
533
534 pub fn from_http(search: String) -> Result<Self> {
536 let mut query = Self::new();
537 let trimmed_search = search.trim_start_matches(QUESTION).trim();
538
539 if trimmed_search.is_empty() {
540 return Ok(query);
541 }
542
543 for k_v in trimmed_search.split(AMPERSAND) {
544 let trimmed_kv = k_v.trim();
545 if trimmed_kv.is_empty() {
546 continue;
547 }
548
549 let mut parts = trimmed_kv.splitn(2, EQUAL);
550 if let (Some(key), Some(value)) = (parts.next(), parts.next()) {
551 let trimmed_key = key.trim();
552 let trimmed_value = value.trim();
553
554 if trimmed_key.is_empty() || trimmed_value.is_empty() {
555 continue;
556 }
557
558 match trimmed_key {
559 Parameters::ORDER => {
560 if !trimmed_value.contains(COLON) {
562 return Err(Error::InvalidSortField(trimmed_value.into()));
564 }
565
566 if let Ok(sort_fields) = SortFields::from_str(trimmed_value) {
567 query.sort_fields = sort_fields;
568 }
569 }
571 Parameters::LIMIT => {
572 query.limit = trimmed_value.parse().unwrap_or(Parameters::DEFAULT_LIMIT);
573 }
574 Parameters::OFFSET => {
575 query.offset = trimmed_value.parse().unwrap_or(Parameters::DEFAULT_OFFSET);
576 }
577 _k => {
578 if trimmed_value.contains(COLON) {
580 let (similarity, values) = parse_parameter(trimmed_value)?;
582 if values.is_empty() {
584 continue;
585 }
586 query
588 .parameters
589 .0
590 .insert(trimmed_key.to_string(), (similarity, values));
591 } else {
592 let decoded_value = url_decode(trimmed_value);
594
595 if let Some((existing_similarity, existing_values)) =
597 query.parameters.0.get_mut(&trimmed_key.to_string())
598 {
599 if *existing_similarity == Similarity::Equals {
601 existing_values.push(decoded_value);
602 }
603 } else {
605 query.parameters.0.insert(
607 trimmed_key.to_string(),
608 (Similarity::Equals, vec![decoded_value]),
609 );
610 }
611 }
612 }
613 }
614 } else {
615 return Err(Error::InvalidSearchParameters(search));
616 }
617 }
618
619 Ok(query)
620 }
621
622 #[cfg(feature = "sql")]
623 pub fn to_sql(&self) -> String {
624 let mut sql_parts = Vec::new();
625
626 let where_clause = self.build_where_clause();
628 if !where_clause.is_empty() {
629 sql_parts.push(format!("WHERE {}", where_clause));
630 }
631
632 let order_clause = self.build_order_clause();
634 if !order_clause.is_empty() {
635 sql_parts.push(format!("ORDER BY {}", order_clause));
636 }
637
638 sql_parts.push(format!("LIMIT ? OFFSET ?"));
640
641 sql_parts.join(" ")
642 }
643
644 #[cfg(feature = "sql")]
645 fn build_where_clause(&self) -> String {
646 let mut conditions = Vec::new();
647
648 for (key, (similarity, values)) in &self.parameters.0 {
649 if values.is_empty() {
650 continue;
651 }
652
653 let condition = match similarity {
654 Similarity::Equals => {
655 if values.len() == 1 {
656 if values[0] == "null" {
657 format!("{} IS ?", key)
658 } else {
659 format!("{} = ?", key)
660 }
661 } else {
662 let placeholders = vec!["?"; values.len()].join(", ");
663 format!("{} IN ({})", key, placeholders)
664 }
665 }
666 Similarity::Contains => {
667 if values.len() == 1 {
668 format!("{} LIKE ?", key)
669 } else {
670 let like_conditions: Vec<String> =
671 values.iter().map(|_| format!("{} LIKE ?", key)).collect();
672 format!("({})", like_conditions.join(" OR "))
673 }
674 }
675 Similarity::StartsWith => {
676 if values.len() == 1 {
677 format!("{} LIKE ?", key)
678 } else {
679 let like_conditions: Vec<String> =
680 values.iter().map(|_| format!("{} LIKE ?", key)).collect();
681 format!("({})", like_conditions.join(" OR "))
682 }
683 }
684 Similarity::EndsWith => {
685 if values.len() == 1 {
686 format!("{} LIKE ?", key)
687 } else {
688 let like_conditions: Vec<String> =
689 values.iter().map(|_| format!("{} LIKE ?", key)).collect();
690 format!("({})", like_conditions.join(" OR "))
691 }
692 }
693 Similarity::Between => {
694 if values.len() >= 2 {
695 let pairs: Vec<&[String]> = values.chunks(2).collect();
697 let between_conditions: Vec<String> = pairs
698 .iter()
699 .map(|pair| {
700 if pair.len() == 2 {
701 format!("{} BETWEEN ? AND ?", key)
702 } else {
703 String::new() }
705 })
706 .filter(|condition| !condition.is_empty())
707 .collect();
708
709 if between_conditions.is_empty() {
710 continue; } else if between_conditions.len() == 1 {
712 between_conditions[0].clone()
713 } else {
714 format!("({})", between_conditions.join(" OR "))
715 }
716 } else {
717 continue; }
719 }
720 Similarity::Lesser => {
721 if values.len() == 1 {
722 format!("{} < ?", key)
723 } else {
724 let conditions: Vec<String> =
725 values.iter().map(|_| format!("{} < ?", key)).collect();
726 format!("({})", conditions.join(" OR "))
727 }
728 }
729 Similarity::LesserOrEqual => {
730 if values.len() == 1 {
731 format!("{} <= ?", key)
732 } else {
733 let conditions: Vec<String> =
734 values.iter().map(|_| format!("{} <= ?", key)).collect();
735 format!("({})", conditions.join(" OR "))
736 }
737 }
738 Similarity::Greater => {
739 if values.len() == 1 {
740 format!("{} > ?", key)
741 } else {
742 let conditions: Vec<String> =
743 values.iter().map(|_| format!("{} > ?", key)).collect();
744 format!("({})", conditions.join(" OR "))
745 }
746 }
747 Similarity::GreaterOrEqual => {
748 if values.len() == 1 {
749 format!("{} >= ?", key)
750 } else {
751 let conditions: Vec<String> =
752 values.iter().map(|_| format!("{} >= ?", key)).collect();
753 format!("({})", conditions.join(" OR "))
754 }
755 }
756 };
757
758 conditions.push(condition);
759 }
760
761 conditions.join(" AND ")
762 }
763
764 #[cfg(feature = "sql")]
765 fn build_order_clause(&self) -> String {
766 let mut order_parts = Vec::new();
767
768 for (name, order) in &self.sort_fields.0 {
769 if !name.is_empty() {
770 let direction = match order {
771 SortOrder::Ascending => "ASC",
772 SortOrder::Descending => "DESC",
773 };
774 order_parts.push(format!("{} {}", name, direction));
775 }
776 }
777
778 order_parts.join(", ")
779 }
780
781 #[cfg(feature = "sql")]
782 pub fn to_values(&self) -> Result<Vec<SqlValue>> {
783 let mut sql_values = Vec::new();
784
785 for k in self.parameters.inner().keys() {
786 let (param_similarity, param_values) = self.parameters.inner().get(k).unwrap();
787
788 for cur_val in param_values {
789 if cur_val.trim().is_empty() {
791 continue;
792 }
793
794 if cur_val == "null" {
795 sql_values.push(SqlValue::Null);
796 continue;
797 }
798
799 let sql_value = match *param_similarity {
800 Similarity::Contains => SqlValue::Text(format!("%{}%", cur_val)),
801 Similarity::StartsWith => SqlValue::Text(format!("{}%", cur_val)),
802 Similarity::EndsWith => SqlValue::Text(format!("%{}", cur_val)),
803 _ => {
804 if let Ok(i) = cur_val.parse::<i64>() {
806 SqlValue::Integer(i)
807 } else if let Ok(f) = cur_val.parse::<f64>() {
808 SqlValue::Real(f)
809 } else {
810 SqlValue::Text(cur_val.clone())
811 }
812 }
813 };
814
815 sql_values.push(sql_value);
816 }
817 }
818
819 sql_values.push(SqlValue::Integer(self.limit as i64));
821 sql_values.push(SqlValue::Integer(self.offset as i64));
822
823 Ok(sql_values)
824 }
825
826 #[cfg(feature = "sql")]
827 pub fn to_parameter_values(&self) -> Result<Vec<SqlValue>> {
829 let mut sql_values = Vec::new();
830
831 for k in self.parameters.inner().keys() {
832 let (param_similarity, param_values) =
833 self.parameters.inner().get(k).ok_or_else(|| {
834 Error::InvalidSqlValue(format!("Parameter '{}' not found", k))
835 })?;
836
837 for cur_val in param_values {
838 if cur_val.trim().is_empty() {
840 continue;
841 }
842
843 if cur_val == "null" {
844 sql_values.push(SqlValue::Null);
845 continue;
846 }
847
848 let sql_value = match *param_similarity {
849 Similarity::Contains => SqlValue::Text(format!("%{}%", cur_val)),
850 Similarity::StartsWith => SqlValue::Text(format!("{}%", cur_val)),
851 Similarity::EndsWith => SqlValue::Text(format!("%{}", cur_val)),
852 _ => {
853 if let Ok(i) = cur_val.parse::<i64>() {
855 SqlValue::Integer(i)
856 } else if let Ok(f) = cur_val.parse::<f64>() {
857 SqlValue::Real(f)
858 } else {
859 SqlValue::Text(cur_val.clone())
860 }
861 }
862 };
863
864 sql_values.push(sql_value);
865 }
866 }
867
868 Ok(sql_values)
869 }
870
871 #[cfg(feature = "sql")]
872 pub fn to_pagination_values(&self) -> Vec<SqlValue> {
874 vec![
875 SqlValue::Integer(self.limit as i64),
876 SqlValue::Integer(self.offset as i64),
877 ]
878 }
879
880 #[cfg(feature = "sql")]
881 pub fn total_parameters(&self) -> usize {
884 let parameter_count: usize = self
885 .parameters
886 .inner()
887 .values()
888 .map(|(_, values)| values.iter().filter(|v| !v.trim().is_empty()).count())
889 .sum();
890
891 parameter_count + 2 }
893}