pokemon_tcg_api_client/builder/
mod.rs1pub mod energy;
3pub mod pokemon;
4pub mod set;
5pub mod trainer;
6
7use std::{collections::HashMap, fmt::Display};
8
9#[derive(Clone)]
11pub enum Ordering {
12 Ascending(String),
13 Descending(String),
14}
15
16pub trait QueryBuilder {
18 fn new() -> Self;
20 fn page(&self) -> Option<u32>;
22 fn page_size(&self) -> Option<u8>;
24 fn filters(&self) -> HashMap<String, String>;
26 fn order_by(&self) -> Vec<Ordering>;
28 fn select_fields(&self) -> Vec<String>;
30
31 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}