use super::Guild;
use crate::{BoardGameGeekApi, IntoQueryParam, QueryParam, Result};
#[derive(Clone, Debug)]
pub enum GuildMemberSortBy {
Username,
DateJoined,
}
impl IntoQueryParam for GuildMemberSortBy {
fn into_query_param(self, key: &str) -> QueryParam<'_> {
match self {
Self::Username => (key, "username".to_owned()),
Self::DateJoined => (key, "date".to_owned()),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct GuildQueryParams {
include_member_page: Option<u64>,
sort_by: Option<GuildMemberSortBy>,
}
impl GuildQueryParams {
pub fn new() -> Self {
Self::default()
}
pub fn include_member_page(mut self, member_page: u64) -> Self {
self.include_member_page = Some(member_page);
self
}
pub fn sort_by(mut self, sort_by: GuildMemberSortBy) -> Self {
self.sort_by = Some(sort_by);
self
}
}
#[derive(Clone, Debug)]
struct GuildQueryBuilder {
guild_id: u64,
params: GuildQueryParams,
}
impl<'builder> GuildQueryBuilder {
fn new(guild_id: u64, params: GuildQueryParams) -> Self {
Self { guild_id, params }
}
fn build(self) -> Vec<QueryParam<'builder>> {
let mut query_params: Vec<_> = vec![];
query_params.push(self.guild_id.into_query_param("id"));
if let Some(member_page) = self.params.include_member_page {
query_params.push(true.into_query_param("members"));
query_params.push(member_page.into_query_param("page"));
}
if let Some(sort_by) = self.params.sort_by {
query_params.push(sort_by.into_query_param("sort"));
}
query_params
}
}
pub struct GuildApi<'api> {
pub(crate) api: &'api BoardGameGeekApi,
endpoint: &'static str,
}
impl<'api> GuildApi<'api> {
pub(crate) fn new(api: &'api BoardGameGeekApi) -> Self {
Self {
api,
endpoint: "guild",
}
}
pub async fn get(&self, guild_id: u64, query_params: GuildQueryParams) -> Result<Guild> {
let query = GuildQueryBuilder::new(guild_id, query_params);
let request = self.api.build_request(self.endpoint, &query.build());
self.api.execute_request::<Guild>(request).await
}
}
#[cfg(test)]
mod tests {
use chrono::{TimeZone, Utc};
use mockito::Matcher;
use super::*;
use crate::{Guild, Location, Member, MemberPage};
#[tokio::test]
async fn get_by_id() {
let mut server = mockito::Server::new_async().await;
let api = BoardGameGeekApi {
base_url: server.url(),
client: reqwest::Client::new(),
};
let mock = server
.mock("GET", "/guild")
.match_query(Matcher::AllOf(vec![Matcher::UrlEncoded(
"id".to_owned(),
"13".to_owned(),
)]))
.with_status(200)
.with_body(
std::fs::read_to_string("test_data/guild/guild.xml")
.expect("failed to load test data"),
)
.create_async()
.await;
let guild = api.guild().get(13, GuildQueryParams::new()).await;
mock.assert_async().await;
assert!(guild.is_ok(), "error returned when okay expected");
let guild = guild.unwrap();
assert_eq!(
guild,
Guild {
id: 13,
name: "Con of the North".to_owned(),
created_at: Utc.with_ymd_and_hms(2007, 6, 14, 1, 6, 46).unwrap(),
category: "event".to_owned(),
website: "http://www.website.org/".to_owned(),
manager: "ManagerName".to_owned(),
description:
"A group to discuss the Con of the North, held in February in Minnesota."
.to_owned(),
location: Location {
address_line_1: "".to_owned(),
address_line_2: "".to_owned(),
city: "Saint Paul".to_owned(),
state: "Minnesota".to_owned(),
country: "United States".to_owned(),
postal_code: "".to_owned(),
},
member_page: None,
},
);
}
#[tokio::test]
async fn get_with_member_page() {
let mut server = mockito::Server::new_async().await;
let api = BoardGameGeekApi {
base_url: server.url(),
client: reqwest::Client::new(),
};
let mock = server
.mock("GET", "/guild")
.match_query(Matcher::AllOf(vec![
Matcher::UrlEncoded("id".to_owned(), "13".to_owned()),
Matcher::UrlEncoded("members".to_owned(), "1".to_owned()),
Matcher::UrlEncoded("page".to_owned(), "2".to_owned()),
]))
.with_status(200)
.with_body(
std::fs::read_to_string("test_data/guild/guild_with_member_page.xml")
.expect("failed to load test data"),
)
.create_async()
.await;
let guild = api
.guild()
.get(13, GuildQueryParams::new().include_member_page(2))
.await;
mock.assert_async().await;
assert!(guild.is_ok(), "error returned when okay expected");
let guild = guild.unwrap();
assert_eq!(
guild,
Guild {
id: 13,
name: "Con of the North".to_owned(),
created_at: Utc.with_ymd_and_hms(2007, 6, 14, 1, 6, 46).unwrap(),
category: "event".to_owned(),
website: "http://www.website.org/".to_owned(),
manager: "ManagerName".to_owned(),
description:
"A group to discuss the Con of the North, held in February in Minnesota."
.to_owned(),
location: Location {
address_line_1: "".to_owned(),
address_line_2: "".to_owned(),
city: "Saint Paul".to_owned(),
state: "Minnesota".to_owned(),
country: "United States".to_owned(),
postal_code: "".to_owned(),
},
member_page: Some(MemberPage {
total_members: 27,
page_number: 2,
members: vec![
Member {
name: "SomeMember".to_owned(),
date_joined: Utc.with_ymd_and_hms(2016, 5, 19, 10, 53, 1).unwrap(),
},
Member {
name: "SomeOtherMember".to_owned(),
date_joined: Utc.with_ymd_and_hms(2009, 4, 24, 0, 1, 22).unwrap(),
},
],
}),
},
);
}
#[tokio::test]
async fn get_from_query_params() {
let mut server = mockito::Server::new_async().await;
let api = BoardGameGeekApi {
base_url: server.url(),
client: reqwest::Client::new(),
};
let mock = server
.mock("GET", "/guild")
.match_query(Matcher::AllOf(vec![
Matcher::UrlEncoded("id".to_owned(), "13".to_owned()),
Matcher::UrlEncoded("members".to_owned(), "1".to_owned()),
Matcher::UrlEncoded("page".to_owned(), "5".to_owned()),
Matcher::UrlEncoded("sort".to_owned(), "date".to_owned()),
]))
.with_status(200)
.with_body(
std::fs::read_to_string("test_data/guild/guild_with_member_page.xml")
.expect("failed to load test data"),
)
.create_async()
.await;
let params = GuildQueryParams::new()
.include_member_page(5)
.sort_by(GuildMemberSortBy::DateJoined);
let guild = api.guild().get(13, params).await;
mock.assert_async().await;
assert!(guild.is_ok(), "error returned when okay expected");
}
}