pokemon_tcg_api_client/builder/
mod.rs

1//! Contains the implementation for the query builders used for advanced querying to the api.
2pub mod energy;
3pub mod pokemon;
4pub mod set;
5pub mod trainer;
6
7use std::{collections::HashMap, fmt::Display};
8
9/// Container for the ordering of query fields of the results.
10#[derive(Clone)]
11pub enum Ordering {
12    Ascending(String),
13    Descending(String),
14}
15
16/// Trait for implementing query builders for the api client.
17pub trait QueryBuilder {
18    /// Creates a new instance of the query builder.
19    fn new() -> Self;
20    /// Returns the current page the query builder is set to.
21    fn page(&self) -> Option<u32>;
22    /// Returns the current page size the query builder is set to.
23    fn page_size(&self) -> Option<u8>;
24    /// Returns the filters set for the query builder.
25    fn filters(&self) -> HashMap<String, String>;
26    /// Returns the ordering information of the query builder.
27    fn order_by(&self) -> Vec<Ordering>;
28    /// Returns the selected fields for the returning json of the query builder.
29    fn select_fields(&self) -> Vec<String>;
30
31    /// Builds and returns the query url for the api request.
32    ///
33    /// * `url` - The base url of the query url.
34    fn build(&self, url: &str) -> String {
35        let url = url;
36        let mut query_index = url.find(|x| x == '?');
37
38        let mut builder = String::from(url);
39
40        if let Some(page_size) = self.page_size() {
41            build_query_param(&mut builder, &mut query_index, "pageSize", page_size);
42        }
43        if let Some(page) = self.page() {
44            build_query_param(&mut builder, &mut query_index, "page", page);
45        }
46
47        let order_by = self.order_by();
48
49        if !order_by.is_empty() {
50            todo!()
51        }
52
53        let fields = self.select_fields();
54
55        if !fields.is_empty() {
56            let select_query = fields
57                .iter()
58                .map(|x| format!("{x},"))
59                .collect::<Vec<String>>()
60                .concat()
61                .trim_end_matches(',')
62                .to_owned();
63
64            build_query_param(&mut builder, &mut query_index, "select", select_query);
65        }
66
67        let filters = self.filters();
68
69        if !filters.is_empty() {
70            builder += match query_index {
71                Some(_) => "&",
72                None => "?",
73            };
74            builder += &urlencoding::encode("q");
75            builder += "=";
76            query_index = None;
77
78            build_filter_query(query_index, &mut builder, &filters);
79        }
80
81        builder
82    }
83}
84
85fn build_filter_query(
86    mut query_index: Option<usize>,
87    builder: &mut String,
88    filters: &HashMap<String, String>,
89) {
90    for (filter_key, filter_value) in filters {
91        if query_index.is_some() {
92            *builder += " ";
93        }
94
95        let split = filter_value.split(',').collect::<Vec<&str>>();
96
97        if split.is_empty() {
98            *builder += &urlencoding::encode(filter_key);
99            *builder += ":";
100            *builder += &urlencoding::encode(&enclose_whitespace_strings(filter_value));
101        }
102
103        for sub_split in &split {
104            *builder += &urlencoding::encode(filter_key);
105            *builder += ":";
106            *builder += &urlencoding::encode(&enclose_whitespace_strings(sub_split));
107
108            if let Some(last) = split.last() {
109                if sub_split != last {
110                    *builder += &urlencoding::encode(" or ");
111                }
112            }
113        }
114
115        query_index = Some(0);
116    }
117}
118
119fn build_query_param<T: Display>(
120    builder: &mut String,
121    query_index: &mut Option<usize>,
122    key: &str,
123    value: T,
124) {
125    *builder += match *query_index {
126        Some(_) => "&",
127        None => "?",
128    };
129    *builder += &urlencoding::encode(key);
130    *builder += "=";
131    *builder += &urlencoding::encode(&value.to_string());
132    *query_index = Some(0);
133}
134
135fn enclose_whitespace_strings(value: &str) -> String {
136    if value.chars().any(char::is_whitespace) {
137        return format!("\"{value}\"");
138    }
139
140    value.to_string()
141}