use crate::utils::{self, Params};
use anyhow::Result;
use serde_json::Value;
pub struct Client1 {
pub url_base: String,
pub api_prefix: String,
pub api_key: String,
}
impl Client1 {
pub fn new(api_key: &str, url_base: Option<String>, api_prefix: Option<String>) -> Self {
let ub;
let prefix;
let api_key = api_key.to_string();
if let Some(u) = url_base {
ub = match u.strip_suffix('/') {
Some(stripped) => stripped.to_string(),
None => u,
};
} else {
ub = "https://boardgamegeek.com".to_string();
}
if let Some(p) = api_prefix {
prefix = p.as_str().trim_matches('/').to_string();
} else {
prefix = "xmlapi".to_string();
}
return Self {
url_base: ub,
api_prefix: prefix,
api_key: api_key,
};
}
pub fn new_from_defaults(api_key: &str) -> Self {
return Self::new(api_key, None, None);
}
pub async fn search(&self, search: &str, options: Option<Params>) -> Result<Value> {
let url = self.get_full_url(
"search".into(),
options,
Some(Params::from([("search".into(), search.into())])),
None,
);
let data = utils::get_json_resp(&url, &self.api_key).await?;
return Ok(data);
}
pub fn search_b(&self, search: &str, options: Option<Params>) -> Result<Value> {
let url = self.get_full_url(
"search".into(),
options,
Some(Params::from([("search".into(), search.into())])),
None,
);
let data = utils::get_json_resp_b(&url, &self.api_key)?;
return Ok(data);
}
pub async fn boardgame(&self, game_ids: &Vec<usize>, options: Option<Params>) -> Result<Value> {
let ids: Vec<String> = game_ids.iter().map(|i| i.to_string()).collect();
let url = self.get_full_url("boardgame".into(), options, None, Some(&ids));
let data = utils::get_json_resp(&url, &self.api_key).await?;
return Ok(data);
}
pub fn boardgame_b(&self, game_ids: &Vec<usize>, options: Option<Params>) -> Result<Value> {
let ids: Vec<String> = game_ids.iter().map(|i| i.to_string()).collect();
let url = self.get_full_url("boardgame".into(), options, None, Some(&ids));
let data = utils::get_json_resp_b(&url, &self.api_key)?;
return Ok(data);
}
pub async fn collection(&self, username: &str, options: Option<Params>) -> Result<Value> {
let addons = vec![username.to_string()];
let url = self.get_full_url("collection".into(), options, None, Some(&addons));
let data = utils::get_json_resp(&url, &self.api_key).await?;
return Ok(data);
}
pub fn collection_b(&self, username: &str, options: Option<Params>) -> Result<Value> {
let addons = vec![username.to_string()];
let url = self.get_full_url("collection".into(), options, None, Some(&addons));
let data = utils::get_json_resp_b(&url, &self.api_key)?;
return Ok(data);
}
pub async fn thread(&self, thread_id: usize, options: Option<Params>) -> Result<Value> {
let addons = vec![thread_id.to_string()];
let url = self.get_full_url("thread".into(), options, None, Some(&addons));
let data = utils::get_json_resp(&url, &self.api_key).await?;
return Ok(data);
}
pub fn thread_b(&self, thread_id: usize, options: Option<Params>) -> Result<Value> {
let addons = vec![thread_id.to_string()];
let url = self.get_full_url("thread".into(), options, None, Some(&addons));
let data = utils::get_json_resp_b(&url, &self.api_key)?;
return Ok(data);
}
pub async fn geeklist(&self, list_id: usize, options: Option<Params>) -> Result<Value> {
let addons = vec![list_id.to_string()];
let url = self.get_full_url("thread".into(), options, None, Some(&addons));
let data = utils::get_json_resp(&url, &self.api_key).await?;
return Ok(data);
}
pub fn geeklist_b(&self, list_id: usize, options: Option<Params>) -> Result<Value> {
let addons = vec![list_id.to_string()];
let url = self.get_full_url("thread".into(), options, None, Some(&addons));
let data = utils::get_json_resp_b(&url, &self.api_key)?;
return Ok(data);
}
fn gen_url(
&self,
path: &str,
options: Option<Params>,
uri_addons: Option<&Vec<String>>,
) -> String {
let mut ret = String::new();
ret = ret + &self.url_base + "/" + &self.api_prefix + "/" + path;
if let Some(addons) = uri_addons {
ret = ret + "/" + &addons.join(",");
}
ret += "?";
if let Some(opts) = options {
let qs = utils::params2qs(&opts);
ret.push_str(&qs);
}
return ret;
}
fn get_full_url(
&self,
path: String,
params: Option<Params>,
default_params: Option<Params>,
uri_addons: Option<&Vec<String>>,
) -> String {
let mut opts = utils::get_opts(params);
if let Some(def_params) = default_params {
for (k, v) in &def_params {
opts.insert(k.into(), v.into());
}
}
let url = self.gen_url(&path, Some(opts), uri_addons);
return url;
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
fn get_api_key() -> String {
return env::var("BGG_API").unwrap();
}
#[test]
fn test_client() {
let cl = Client1::new_from_defaults(&get_api_key());
assert_eq!(cl.url_base, "https://boardgamegeek.com".to_string());
assert_eq!(cl.api_prefix, "xmlapi".to_string());
let base = "https://example.com";
let prefix = "/blah";
let cl = Client1::new(&get_api_key(), Some(base.to_string()), Some(prefix.to_string()));
assert_eq!(cl.url_base, base.to_string());
assert_eq!(cl.api_prefix, "blah");
}
#[test]
fn test_gen_url() {
let cl = Client1::new_from_defaults(&get_api_key());
let params = Params::from([
("search".to_string(), "this is a search".to_string()),
("exact".to_string(), "1".to_string()),
]);
let res = cl.gen_url("search", Some(params), None);
println!("{}", res);
assert!(res.starts_with("https://boardgamegeek.com/xmlapi/search?"));
assert!(res.contains("search=this%20is%20a%20search"));
assert!(res.contains("&"));
assert!(res.contains("exact=1"));
let res = cl.gen_url("boardgame", None, Some(&vec!["1".into(), "2".into()]));
assert_eq!(
res,
"https://boardgamegeek.com/xmlapi/boardgame/1,2?".to_string()
);
}
#[test]
fn test_get_full_url() {
let cl = Client1::new_from_defaults(&get_api_key());
let url = cl.get_full_url(
"search".to_string(),
None,
Some(Params::from([("search".into(), "this is a search".into())])),
None,
);
assert_eq!(
url,
"https://boardgamegeek.com/xmlapi/search?search=this%20is%20a%20search"
);
let url = cl.get_full_url(
"boardgame".into(),
Some(Params::from([("comments".into(), "1".into())])),
None,
Some(&vec!["a".into(), "b".into(), "c".into()]),
);
assert_eq!(
url,
"https://boardgamegeek.com/xmlapi/boardgame/a,b,c?comments=1".to_string()
);
}
}