@@ -8,10 +8,14 @@ description = "An unofficial collection of APIs used in FreeJam games and mods" | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
[dependencies] | |||
serde = { version = "1.0", features = ["derive"]} | |||
serde_json = "1.0" | |||
reqwest = { version = "0.11.1", features = ["json"]} | |||
url = "2.2.1" | |||
serde = { version = "^1", features = ["derive"]} | |||
serde_json = "^1" | |||
reqwest = { version = "^0.11", features = ["json"]} | |||
url = "^2.2" | |||
ureq = { version = "^2", features = ["json"], optional = true} | |||
[dev-dependencies] | |||
tokio = { version = "1.4.0", features = ["macros"]} | |||
tokio = { version = "1.4.0", features = ["macros"]} | |||
[features] | |||
simple = ["ureq"] |
@@ -0,0 +1 @@ | |||
// TODO |
@@ -1,2 +1,6 @@ | |||
pub mod cardlife; | |||
pub mod robocraft; | |||
#[cfg(feature = "simple")] | |||
pub mod robocraft_simple; | |||
#[cfg(feature = "simple")] | |||
pub mod cardlife_simple; |
@@ -4,7 +4,7 @@ use url::{Url}; | |||
use crate::robocraft::{ITokenProvider, DefaultTokenProvider, FactoryInfo, FactorySearchBuilder, RoboShopItemsInfo, FactoryRobotGetInfo}; | |||
use crate::robocraft::factory_json::ListPayload; | |||
const FACTORY_DOMAIN: &str = "https://factory.robocraftgame.com/"; | |||
pub const FACTORY_DOMAIN: &str = "https://factory.robocraftgame.com/"; | |||
pub struct FactoryAPI { | |||
client: Client, | |||
@@ -1,9 +1,11 @@ | |||
mod factory; | |||
mod factory_json; | |||
mod factory_request_builder; | |||
pub use self::factory::{FactoryAPI}; | |||
pub use self::factory::{FactoryAPI, FACTORY_DOMAIN}; | |||
pub use self::factory_json::{FactoryInfo, FactoryRobotListInfo, RoboShopItemsInfo, FactoryRobotGetInfo}; | |||
pub use self::factory_request_builder::{FactorySearchBuilder, FactoryMovementType, FactoryOrderType, FactoryWeaponType, FactoryTextSearchType}; | |||
#[cfg(feature = "simple")] | |||
pub(crate) use self::factory_json::{ListPayload}; | |||
mod auth; | |||
pub use self::auth::{ITokenProvider, DefaultTokenProvider}; | |||
@@ -0,0 +1,76 @@ | |||
use ureq::{Agent, Error, Response}; | |||
use url::Url; | |||
use serde_json::{to_string}; | |||
use crate::robocraft::{ITokenProvider, DefaultTokenProvider, FACTORY_DOMAIN, FactoryInfo, RoboShopItemsInfo, FactoryRobotGetInfo}; | |||
use crate::robocraft::{ListPayload}; | |||
use crate::robocraft_simple::FactorySearchBuilder; | |||
pub struct FactoryAPI { | |||
client: Agent, | |||
token: Box<dyn ITokenProvider>, | |||
} | |||
impl FactoryAPI { | |||
pub fn new() -> FactoryAPI { | |||
FactoryAPI { | |||
client: Agent::new(), | |||
token: Box::new(DefaultTokenProvider{}), | |||
} | |||
} | |||
pub fn list(&self) -> Result<FactoryInfo<RoboShopItemsInfo>, Error> { | |||
let url = Url::parse(FACTORY_DOMAIN) | |||
.unwrap() | |||
.join("/api/roboShopItems/list") | |||
.unwrap(); | |||
let payload = ListPayload::default(); | |||
let mut request_builder = self.client.post(url.as_str()) | |||
.set("Content-Type", "application/json"); | |||
if let Ok(token) = self.token.token() { | |||
request_builder = request_builder.set("Authorization", &("Web ".to_owned() + &token)); | |||
} | |||
let result = request_builder.send_string(&to_string(&payload).unwrap()); | |||
if let Ok(response) = result { | |||
let json_res = response.into_json::<FactoryInfo<RoboShopItemsInfo>>(); | |||
if let Ok(json) = json_res { | |||
return Ok(json); | |||
} | |||
return Err(Error::Status(500, Response::new(500, "Malformed JSON", "").unwrap())); // server returned malformed data | |||
} | |||
Err(result.err().unwrap()) | |||
} | |||
pub fn list_builder(&self) -> FactorySearchBuilder { | |||
let url = Url::parse(FACTORY_DOMAIN) | |||
.unwrap() | |||
.join("/api/roboShopItems/list") | |||
.unwrap(); | |||
let mut token_opt = None; | |||
if let Ok(token) = self.token.token() { | |||
token_opt = Some(token); | |||
} | |||
let request_builder = self.client.post(url.as_str()); | |||
FactorySearchBuilder::new(request_builder, token_opt) | |||
} | |||
pub fn get(&self, item_id: usize) -> Result<FactoryInfo<FactoryRobotGetInfo>, Error> { | |||
let url = Url::parse(FACTORY_DOMAIN) | |||
.unwrap() | |||
.join(&format!("/api/roboShopItems/get/{}", item_id)) | |||
.unwrap(); | |||
let mut request_builder = self.client.get(url.as_str()); | |||
if let Ok(token) = self.token.token() { | |||
request_builder = request_builder.set("Authorization", &("Web ".to_owned() + &token)); | |||
} | |||
let result = request_builder.call(); | |||
if let Ok(response) = result { | |||
let json_res = response.into_json::<FactoryInfo<FactoryRobotGetInfo>>(); | |||
if let Ok(json) = json_res { | |||
return Ok(json); | |||
} | |||
return Err(Error::Status(500, Response::new(500, "Malformed JSON", "").unwrap())); // server returned malformed data | |||
} | |||
Err(result.err().unwrap()) | |||
} | |||
} |
@@ -0,0 +1,131 @@ | |||
use ureq::{Request, Response, Error}; | |||
use crate::robocraft::{FactoryInfo, RoboShopItemsInfo, FactoryTextSearchType, FactoryWeaponType, FactoryMovementType, FactoryOrderType}; | |||
use crate::robocraft::{ListPayload}; | |||
pub struct FactorySearchBuilder { | |||
reqwest_builder: Request, | |||
payload: ListPayload, | |||
token: Option<String>, | |||
} | |||
impl FactorySearchBuilder { | |||
pub(crate) fn new(request_builder: Request, token: Option<String>) -> FactorySearchBuilder { | |||
FactorySearchBuilder { | |||
reqwest_builder: request_builder.set("Content-Type", "application/json"), | |||
payload: ListPayload::empty(), | |||
token, | |||
} | |||
} | |||
pub fn page(mut self, page_number: isize) -> Self { | |||
self.payload.page = page_number; | |||
self | |||
} | |||
pub fn items_per_page(mut self, page_size: isize) -> Self { | |||
self.payload.page_size = page_size; | |||
self | |||
} | |||
pub fn order(mut self, order_type: FactoryOrderType) -> Self { | |||
self.payload.order = order_type as isize; | |||
self | |||
} | |||
/* // this appears to not do anything (removed to prevent confusion) | |||
// use text_search_type(FactoryTextSearchType::Player) instead | |||
pub fn players_only(mut self, p: bool) -> Self { | |||
self.payload.player_filter = p; | |||
self | |||
} | |||
*/ | |||
pub fn movement_or(mut self, movement_type: FactoryMovementType) -> Self { | |||
if self.payload.movement_filter == "" { | |||
self.payload.movement_filter = format!("{},{}", &self.payload.movement_filter, movement_type as isize); | |||
} else { | |||
self.payload.movement_filter = (movement_type as isize).to_string(); | |||
} | |||
self.payload.movement_category_filter = self.payload.movement_filter.clone(); | |||
self | |||
} | |||
pub fn weapon_or(mut self, weapon_type: FactoryWeaponType) -> Self { | |||
if self.payload.weapon_filter == "" { | |||
self.payload.weapon_filter = format!("{},{}", &self.payload.weapon_filter, weapon_type as isize); | |||
} else { | |||
self.payload.weapon_filter = (weapon_type as isize).to_string(); | |||
} | |||
self.payload.weapon_category_filter = self.payload.weapon_filter.clone(); | |||
self | |||
} | |||
pub fn cpu_range(mut self, min: isize, max: isize) -> Self { | |||
self.payload.minimum_cpu = min; | |||
self.payload.maximum_cpu = max; | |||
self | |||
} | |||
pub fn min_cpu(mut self, min: isize) -> Self { | |||
self.payload.minimum_cpu = min; | |||
self | |||
} | |||
pub fn max_cpu(mut self, max: isize) -> Self { | |||
self.payload.maximum_cpu = max; | |||
self | |||
} | |||
pub fn no_minimum_cpu(mut self) -> Self { | |||
self.payload.minimum_cpu = -1; | |||
self | |||
} | |||
pub fn no_maximum_cpu(mut self) -> Self { | |||
self.payload.maximum_cpu = -1; | |||
self | |||
} | |||
pub fn text(mut self, t: String) -> Self { | |||
self.payload.text_filter = t; | |||
self | |||
} | |||
pub fn text_search_type(mut self, search_type: FactoryTextSearchType) -> Self { | |||
self.payload.text_search_field = search_type as isize; | |||
self | |||
} | |||
// setting buyable to false while using the default token provider will cause HTTP status 500 error | |||
pub fn buyable(mut self, b: bool) -> Self { | |||
self.payload.buyable = b; | |||
self | |||
} | |||
pub fn prepend_featured(mut self, b: bool) -> Self { | |||
self.payload.prepend_featured_robot = b; | |||
self | |||
} | |||
pub fn default_page(mut self, b: bool) -> Self { | |||
self.payload.default_page = b; | |||
self | |||
} | |||
pub fn send(mut self) -> Result<FactoryInfo<RoboShopItemsInfo>, Error> { | |||
self.reqwest_builder = self.reqwest_builder; | |||
if let Some(token) = self.token.clone() { | |||
self.reqwest_builder = self.reqwest_builder.set("Authorization", &("Web ".to_owned() + &token)); | |||
} | |||
let result = self.reqwest_builder.send_string(&serde_json::to_string(&self.payload).unwrap()); | |||
if let Ok(response) = result { | |||
let json_res = response.into_json::<FactoryInfo<RoboShopItemsInfo>>(); | |||
if let Ok(json) = json_res { | |||
return Ok(json); | |||
} | |||
return Err(Error::Status(500, Response::new(500, "Malformed JSON", "").unwrap())); // server returned malformed data | |||
} | |||
Err(result.err().unwrap()) | |||
} | |||
} |
@@ -0,0 +1,4 @@ | |||
mod factory; | |||
mod factory_request_builder; | |||
pub use factory::{FactoryAPI}; | |||
pub use factory_request_builder::{FactorySearchBuilder}; |
@@ -0,0 +1,81 @@ | |||
#[cfg(feature = "simple")] | |||
use libfj::robocraft_simple; | |||
#[cfg(feature = "simple")] | |||
use libfj::robocraft; | |||
#[cfg(feature = "simple")] | |||
#[test] | |||
fn robocraft_factory_api_init_simple() -> Result<(), ()> { | |||
robocraft_simple::FactoryAPI::new(); | |||
Ok(()) | |||
} | |||
#[cfg(feature = "simple")] | |||
fn builder() -> robocraft_simple::FactorySearchBuilder { | |||
robocraft_simple::FactoryAPI::new().list_builder() | |||
} | |||
#[cfg(feature = "simple")] | |||
fn assert_factory_list(robo_info: robocraft::FactoryInfo<robocraft::RoboShopItemsInfo>) -> Result<(), ()> { | |||
assert_ne!(robo_info.response.roboshop_items.len(), 0); | |||
assert_eq!(robo_info.status_code, 200); | |||
for robot in &robo_info.response.roboshop_items { | |||
assert_ne!(robot.item_name, ""); | |||
assert_ne!(robot.added_by, ""); | |||
assert_ne!(robot.added_by_display_name, ""); | |||
assert_ne!(robot.thumbnail, ""); | |||
println!("FactoryRobotListInfo.to_string() -> `{}`", robot.to_string()); | |||
} | |||
Ok(()) | |||
} | |||
#[test] | |||
#[cfg(feature = "simple")] | |||
fn robocraft_factory_custom_query_simple() -> Result<(), ()> { | |||
let result = builder() | |||
.movement_or(robocraft::FactoryMovementType::Wheels) | |||
.weapon_or(robocraft::FactoryWeaponType::Laser) | |||
.page(2) | |||
.items_per_page(10) | |||
.send(); | |||
assert!(result.is_ok()); | |||
let robo_info = result.unwrap(); | |||
assert_ne!(robo_info.response.roboshop_items.len(), 0); | |||
//assert_eq!(robo_info.response.roboshop_items.len(), 16); the API behaviour is weird, I swear it's not me! | |||
assert!(robo_info.response.roboshop_items.len() >= 10); | |||
assert_eq!(robo_info.status_code, 200); | |||
for robot in &robo_info.response.roboshop_items { | |||
assert_ne!(robot.item_name, ""); | |||
assert_ne!(robot.added_by, ""); | |||
assert_ne!(robot.added_by_display_name, ""); | |||
assert_ne!(robot.thumbnail, ""); | |||
println!("FactoryRobotListInfo.to_string() -> `{}`", robot.to_string()); | |||
} | |||
Ok(()) | |||
} | |||
#[test] | |||
#[cfg(feature = "simple")] | |||
fn robocraft_factory_player_query() -> Result<(), ()> { | |||
let result = builder() | |||
.text("Baerentoeter".to_string()) | |||
.text_search_type(robocraft::FactoryTextSearchType::Player) | |||
.items_per_page(10) | |||
.send(); | |||
assert!(result.is_ok()); | |||
assert_factory_list(result.unwrap()) | |||
} | |||
#[test] | |||
#[cfg(feature = "simple")] | |||
fn robocraft_factory_robot_query() -> Result<(), ()> { | |||
let api = robocraft_simple::FactoryAPI::new(); | |||
let result = api.get(6478345 /* featured robot id*/); | |||
assert!(result.is_ok()); | |||
let bot_info = result.unwrap(); | |||
assert_ne!(bot_info.response.item_name, ""); | |||
assert_eq!(bot_info.response.item_id, 6478345); | |||
assert_ne!(bot_info.response.cube_data, ""); | |||
assert_ne!(bot_info.response.colour_data, ""); | |||
Ok(()) | |||
} |