Skip to content

Commit d816844

Browse files
authored
AppendUrlQueryPairs & QueryPairsExtension traits (#266)
* Introduce url_query module to help with adding url query parameters * Implement AppendUrlQueryPairs for PostListParams * Add helper macros that implement AsQueryValue for given type * Implement AsQueryValue for post param types * Implement unit tests for QueryPairsExtension
1 parent dc3a5a5 commit d816844

File tree

9 files changed

+318
-264
lines changed

9 files changed

+318
-264
lines changed

wp_api/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub use api_client::{WpApiClient, WpApiRequestBuilder};
44
pub use api_error::{RequestExecutionError, WpApiError, WpErrorCode};
55
pub use parsed_url::{ParseUrlError, ParsedUrl};
66
use plugins::*;
7+
use url_query::AsQueryValue;
78
use users::*;
89
pub use uuid::{WpUuid, WpUuidParseError};
910

@@ -19,6 +20,7 @@ pub mod post_types;
1920
pub mod posts;
2021
pub mod request;
2122
pub mod site_settings;
23+
pub mod url_query;
2224
pub mod users;
2325
pub mod wp_site_health_tests;
2426

@@ -73,6 +75,8 @@ pub enum WpApiParamOrder {
7375
Desc,
7476
}
7577

78+
impl_as_query_value_from_as_str!(WpApiParamOrder);
79+
7680
impl WpApiParamOrder {
7781
fn as_str(&self) -> &str {
7882
match self {

wp_api/src/plugins.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ use std::fmt::Display;
33
use serde::{Deserialize, Serialize};
44
use wp_contextual::WpContextual;
55

6+
use crate::{
7+
impl_as_query_value_from_as_str,
8+
url_query::{AppendUrlQueryPairs, AsQueryValue, QueryPairs, QueryPairsExtension},
9+
};
10+
611
#[derive(Debug, Default, uniffi::Record)]
712
pub struct PluginListParams {
813
/// Limit results to those matching a string.
@@ -13,15 +18,11 @@ pub struct PluginListParams {
1318
pub status: Option<PluginStatus>,
1419
}
1520

16-
impl PluginListParams {
17-
pub fn query_pairs(&self) -> impl IntoIterator<Item = (&str, String)> {
18-
[
19-
("search", self.search.clone()),
20-
("status", self.status.map(|x| x.as_str().to_string())),
21-
]
22-
.into_iter()
23-
// Remove `None` values
24-
.filter_map(|(k, opt_v)| opt_v.map(|v| (k, v)))
21+
impl AppendUrlQueryPairs for PluginListParams {
22+
fn append_query_pairs(&self, query_pairs_mut: &mut QueryPairs) {
23+
query_pairs_mut
24+
.append_option_query_value_pair("search", self.search.as_ref())
25+
.append_option_query_value_pair("status", self.status.as_ref());
2526
}
2627
}
2728

@@ -127,6 +128,8 @@ pub enum PluginStatus {
127128
NetworkActive,
128129
}
129130

131+
impl_as_query_value_from_as_str!(PluginStatus);
132+
130133
impl PluginStatus {
131134
fn as_str(&self) -> &str {
132135
match self {
@@ -150,15 +153,12 @@ mod tests {
150153
use rstest::*;
151154

152155
#[rstest]
153-
#[case(PluginListParams::default(), &[])]
154-
#[case(generate!(PluginListParams, (search, Some("foo".to_string()))), &[("search", "foo")])]
155-
#[case(generate!(PluginListParams, (status, Some(PluginStatus::Active))), &[("status", "active")])]
156-
#[case(generate!(PluginListParams, (search, Some("foo".to_string())), (status, Some(PluginStatus::Inactive))), &[("search", "foo"), ("status", "inactive")])]
156+
#[case(PluginListParams::default(), "")]
157+
#[case(generate!(PluginListParams, (search, Some("foo".to_string()))), "search=foo")]
158+
#[case(generate!(PluginListParams, (status, Some(PluginStatus::Active))), "status=active")]
159+
#[case(generate!(PluginListParams, (search, Some("foo".to_string())), (status, Some(PluginStatus::Inactive))), "search=foo&status=inactive")]
157160
#[trace]
158-
fn test_plugin_list_params(
159-
#[case] params: PluginListParams,
160-
#[case] expected_pairs: &[(&str, &str)],
161-
) {
162-
assert_expected_query_pairs(params.query_pairs(), expected_pairs);
161+
fn test_plugin_list_params(#[case] params: PluginListParams, #[case] expected_query: &str) {
162+
assert_expected_query_pairs(params, expected_query);
163163
}
164164
}

wp_api/src/posts.rs

Lines changed: 42 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use serde::{Deserialize, Serialize};
22
use wp_contextual::WpContextual;
33

4-
use crate::{UserId, WpApiParamOrder};
4+
use crate::{
5+
impl_as_query_value_for_new_type, impl_as_query_value_from_as_str,
6+
url_query::{AppendUrlQueryPairs, AsQueryValue, QueryPairs, QueryPairsExtension},
7+
UserId, WpApiParamOrder,
8+
};
59

610
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, uniffi::Enum)]
711
pub enum WpApiParamPostsOrderBy {
@@ -18,6 +22,8 @@ pub enum WpApiParamPostsOrderBy {
1822
Title,
1923
}
2024

25+
impl_as_query_value_from_as_str!(WpApiParamPostsOrderBy);
26+
2127
impl WpApiParamPostsOrderBy {
2228
fn as_str(&self) -> &str {
2329
match self {
@@ -41,6 +47,8 @@ pub enum WpApiParamPostsTaxRelation {
4147
Or,
4248
}
4349

50+
impl_as_query_value_from_as_str!(WpApiParamPostsTaxRelation);
51+
4452
impl WpApiParamPostsTaxRelation {
4553
fn as_str(&self) -> &str {
4654
match self {
@@ -57,6 +65,8 @@ pub enum WpApiParamPostsSearchColumn {
5765
PostTitle,
5866
}
5967

68+
impl_as_query_value_from_as_str!(WpApiParamPostsSearchColumn);
69+
6070
impl WpApiParamPostsSearchColumn {
6171
fn as_str(&self) -> &str {
6272
match self {
@@ -148,152 +158,46 @@ pub struct PostListParams {
148158
pub sticky: Option<bool>,
149159
}
150160

151-
impl PostListParams {
152-
pub fn query_pairs(&self) -> impl IntoIterator<Item = (&str, String)> {
153-
[
154-
("page", self.page.map(|x| x.to_string())),
155-
("per_page", self.per_page.map(|x| x.to_string())),
156-
("search", self.search.clone()),
157-
("after", self.after.clone()),
158-
("modified_after", self.modified_after.clone()),
159-
(
160-
"author",
161-
(!self.author.is_empty()).then_some(
162-
self.author
163-
.iter()
164-
.map(|x| x.to_string())
165-
.collect::<Vec<String>>()
166-
.join(","),
167-
),
168-
),
169-
(
170-
"author_exclude",
171-
(!self.author_exclude.is_empty()).then_some(
172-
self.author_exclude
173-
.iter()
174-
.map(|x| x.to_string())
175-
.collect::<Vec<String>>()
176-
.join(","),
177-
),
178-
),
179-
("before", self.before.clone()),
180-
("modified_before", self.modified_before.clone()),
181-
(
182-
"exclude",
183-
(!self.exclude.is_empty()).then_some(
184-
self.exclude
185-
.iter()
186-
.map(|x| x.to_string())
187-
.collect::<Vec<String>>()
188-
.join(","),
189-
),
190-
),
191-
(
192-
"include",
193-
(!self.include.is_empty()).then_some(
194-
self.include
195-
.iter()
196-
.map(|x| x.to_string())
197-
.collect::<Vec<String>>()
198-
.join(","),
199-
),
200-
),
201-
("offset", self.offset.map(|x| x.to_string())),
202-
("order", self.order.as_ref().map(|x| x.as_str().to_string())),
203-
(
204-
"orderby",
205-
self.orderby.as_ref().map(|x| x.as_str().to_string()),
206-
),
207-
(
208-
"search_columns",
209-
(!self.search_columns.is_empty()).then_some(
210-
self.search_columns
211-
.iter()
212-
.map(|x| x.as_str().to_string())
213-
.collect::<Vec<String>>()
214-
.join(","),
215-
),
216-
),
217-
(
218-
"slug",
219-
(!self.slug.is_empty()).then_some(
220-
self.slug
221-
.iter()
222-
.map(|x| x.to_string())
223-
.collect::<Vec<String>>()
224-
.join(","),
225-
),
226-
),
227-
(
228-
"status",
229-
(!self.status.is_empty()).then_some(
230-
self.status
231-
.iter()
232-
.map(|x| x.as_str().to_string())
233-
.collect::<Vec<String>>()
234-
.join(","),
235-
),
236-
),
237-
(
238-
"tax_relation",
239-
self.tax_relation.as_ref().map(|x| x.as_str().to_string()),
240-
),
241-
(
242-
"categories",
243-
(!self.categories.is_empty()).then_some(
244-
self.categories
245-
.iter()
246-
.map(|x| x.0.to_string())
247-
.collect::<Vec<String>>()
248-
.join(","),
249-
),
250-
),
251-
(
252-
"categories_exclude",
253-
(!self.categories_exclude.is_empty()).then_some(
254-
self.categories_exclude
255-
.iter()
256-
.map(|x| x.0.to_string())
257-
.collect::<Vec<String>>()
258-
.join(","),
259-
),
260-
),
261-
(
262-
"tags",
263-
(!self.tags.is_empty()).then_some(
264-
self.tags
265-
.iter()
266-
.map(|x| x.0.to_string())
267-
.collect::<Vec<String>>()
268-
.join(","),
269-
),
270-
),
271-
(
272-
"tags_exclude",
273-
(!self.tags_exclude.is_empty()).then_some(
274-
self.tags_exclude
275-
.iter()
276-
.map(|x| x.0.to_string())
277-
.collect::<Vec<String>>()
278-
.join(","),
279-
),
280-
),
281-
("sticky", self.sticky.map(|x| x.to_string())),
282-
]
283-
.into_iter()
284-
// Remove `None` values
285-
.filter_map(|(k, opt_v)| opt_v.map(|v| (k, v)))
161+
impl AppendUrlQueryPairs for PostListParams {
162+
fn append_query_pairs(&self, query_pairs_mut: &mut QueryPairs) {
163+
query_pairs_mut
164+
.append_option_query_value_pair("page", self.page.as_ref())
165+
.append_option_query_value_pair("per_page", self.per_page.as_ref())
166+
.append_option_query_value_pair("search", self.search.as_ref())
167+
.append_option_query_value_pair("after", self.after.as_ref())
168+
.append_option_query_value_pair("modified_after", self.modified_after.as_ref())
169+
.append_vec_query_value_pair("author", &self.author)
170+
.append_vec_query_value_pair("author_exclude", &self.author_exclude)
171+
.append_option_query_value_pair("before", self.before.as_ref())
172+
.append_option_query_value_pair("modified_before", self.modified_before.as_ref())
173+
.append_vec_query_value_pair("exclude", &self.exclude)
174+
.append_vec_query_value_pair("include", &self.include)
175+
.append_option_query_value_pair("offset", self.offset.as_ref())
176+
.append_option_query_value_pair("order", self.order.as_ref())
177+
.append_option_query_value_pair("orderby", self.orderby.as_ref())
178+
.append_vec_query_value_pair("search_columns", &self.search_columns)
179+
.append_vec_query_value_pair("slug", &self.slug)
180+
.append_vec_query_value_pair("status", &self.status)
181+
.append_option_query_value_pair("tax_relation", self.tax_relation.as_ref())
182+
.append_vec_query_value_pair("categories", &self.categories)
183+
.append_vec_query_value_pair("categories_exclude", &self.categories_exclude)
184+
.append_vec_query_value_pair("tags", &self.tags)
185+
.append_vec_query_value_pair("tags_exclude", &self.tags_exclude)
186+
.append_option_query_value_pair("sticky", self.sticky.as_ref());
286187
}
287188
}
288189

190+
impl_as_query_value_for_new_type!(PostId);
289191
uniffi::custom_newtype!(PostId, i32);
290192
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
291193
pub struct PostId(pub i32);
292194

195+
impl_as_query_value_for_new_type!(TagId);
293196
uniffi::custom_newtype!(TagId, i32);
294197
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
295198
pub struct TagId(pub i32);
296199

200+
impl_as_query_value_for_new_type!(CategoryId);
297201
uniffi::custom_newtype!(CategoryId, i32);
298202
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
299203
pub struct CategoryId(pub i32);
@@ -413,6 +317,8 @@ pub enum PostStatus {
413317
Custom(String),
414318
}
415319

320+
impl_as_query_value_from_as_str!(PostStatus);
321+
416322
impl PostStatus {
417323
fn as_str(&self) -> &str {
418324
match self {

wp_api/src/request/endpoint/users_endpoint.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ mod tests {
6666
reassign: UserId(98),
6767
},
6868
),
69-
"/users/54?reassign=98&force=true",
69+
"/users/54?force=true&reassign=98",
7070
);
7171
}
7272

@@ -76,7 +76,7 @@ mod tests {
7676
endpoint.delete_me(&UserDeleteParams {
7777
reassign: UserId(98),
7878
}),
79-
"/users/me?reassign=98&force=true",
79+
"/users/me?force=true&reassign=98",
8080
);
8181
}
8282

wp_api/src/unit_test_common.rs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1+
use crate::url_query::AppendUrlQueryPairs;
2+
use url::Url;
3+
14
#[cfg(test)]
2-
pub fn assert_expected_query_pairs<'a>(
3-
query_pairs: impl IntoIterator<Item = (&'a str, String)>,
4-
expected_pairs: &[(&'a str, &str)],
5-
) {
6-
let mut query_pairs = query_pairs.into_iter().collect::<Vec<_>>();
7-
let mut expected_pairs: Vec<(&str, String)> = expected_pairs
8-
.iter()
9-
.map(|(k, v)| (*k, v.to_string()))
10-
.collect();
11-
// The order of query pairs doesn't matter
12-
query_pairs.sort();
13-
expected_pairs.sort();
14-
assert_eq!(query_pairs, expected_pairs);
5+
pub fn assert_expected_query_pairs(params: impl AppendUrlQueryPairs, expected_query: &str) {
6+
let mut url = Url::parse("https://example.com").unwrap();
7+
params.append_query_pairs(&mut url.query_pairs_mut());
8+
assert_eq!(url.query(), Some(expected_query));
159
}

0 commit comments

Comments
 (0)