An unofficial collection of APIs used in FreeJam games and mods
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
10KB

  1. use std::sync::Mutex;
  2. use reqwest::{Client, Error as ReqwestError, Response};
  3. use url::{Url};
  4. use crate::robocraft2::{ITokenProvider, ErrorPayload};
  5. use crate::robocraft2::{SearchPayload, SearchResponse, CreateRobotPayload, CreateRobotResponse, FactoryInfoResponse, PublishRobotPayload, PublishRobotResponse, MyRobotsResponse, GetRobotResponse};
  6. /// Community Factory Robot 2 root URL
  7. pub const FACTORY_DOMAIN: &str = "https://factory.production.robocraft2.com";
  8. #[derive(Debug)]
  9. pub enum FactoryError {
  10. Protocol(ReqwestError),
  11. Response(ErrorPayload),
  12. ResponseCode(ReqwestError, u16)
  13. }
  14. impl std::fmt::Display for FactoryError {
  15. fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
  16. match self {
  17. Self::Protocol(p) => if let Some(status) = p.status() {
  18. write!(f, "HTTP Error {}: {}", status, p)
  19. } else {
  20. write!(f, "HTTP Error: {}", p)
  21. }
  22. Self::Response(r) => write!(f, "Factory Error #{}: {}", r.error, r.error_message),
  23. Self::ResponseCode(p, s) => write!(f, "HTTP Error {}: {}", s, p)
  24. }
  25. }
  26. }
  27. impl std::error::Error for FactoryError {}
  28. async fn handle_json_response<D: for<'a> serde::Deserialize<'a>>(response: Response) -> Result<D, FactoryError> {
  29. let status_code: u16 = response.status().into();
  30. if status_code > 199 && status_code < 300 {
  31. Ok(response.json::<D>().await.map_err(FactoryError::Protocol)?)
  32. } else {
  33. match response.json::<ErrorPayload>().await {
  34. Ok(err) => Err(FactoryError::Response(err)),
  35. Err(e) => Err(FactoryError::ResponseCode(e, status_code))
  36. }
  37. }
  38. }
  39. /// CRF API implementation
  40. pub struct FactoryAPI {
  41. client: Client,
  42. token: Mutex<Box<dyn ITokenProvider + Send>>,
  43. }
  44. impl FactoryAPI {
  45. /*/// Create a new instance, using `DefaultTokenProvider`.
  46. pub fn new() -> FactoryAPI {
  47. FactoryAPI {
  48. client: Client::new(),
  49. token: Box::new(DefaultTokenProvider{}),
  50. }
  51. }*/
  52. /// Create a new instance using the provided token provider.
  53. pub fn with_auth(token_provider: Box<dyn ITokenProvider + Send>) -> FactoryAPI {
  54. FactoryAPI {
  55. client: Client::new(),
  56. token: Mutex::new(token_provider),
  57. }
  58. }
  59. /// Retrieve CRF robots on the main page.
  60. pub async fn list(&self) -> Result<SearchResponse, FactoryError> {
  61. self.search(SearchPayload::default()).await
  62. }
  63. /// Search for robots on the CRF which meet the provided parameters
  64. pub async fn search(&self, params: SearchPayload) -> Result<SearchResponse, FactoryError> {
  65. let mut url = Url::parse(FACTORY_DOMAIN)
  66. .unwrap()
  67. .join("/v1/foundry/search")
  68. .unwrap();
  69. if let Some(text) = &params.text {
  70. url.query_pairs_mut().append_pair("text", text);
  71. }
  72. if let Some(base_minimum_cpu) = params.base_minimum_cpu {
  73. url.query_pairs_mut().append_pair("baseCpuMinimum", &base_minimum_cpu.to_string());
  74. }
  75. if let Some(base_maximum_cpu) = &params.base_maximum_cpu {
  76. url.query_pairs_mut().append_pair("baseCpuMaximum", &base_maximum_cpu.to_string());
  77. }
  78. if let Some(x) = &params.weapon_minimum_cpu {
  79. url.query_pairs_mut().append_pair("weaponCpuMinimum", &x.to_string());
  80. }
  81. if let Some(x) = &params.weapon_maximum_cpu {
  82. url.query_pairs_mut().append_pair("weaponCpuMaximum", &x.to_string());
  83. }
  84. if let Some(x) = &params.cosmetic_minimum_cpu {
  85. url.query_pairs_mut().append_pair("cosmeticCpuMinimum", &x.to_string());
  86. }
  87. if let Some(x) = &params.cosmetic_maximum_cpu {
  88. url.query_pairs_mut().append_pair("cosmeticCpuMaximum", &x.to_string());
  89. }
  90. if let Some(x) = &params.cluster_minimum {
  91. url.query_pairs_mut().append_pair("clusterMinimum", &x.to_string());
  92. }
  93. if let Some(x) = &params.cluster_maximum {
  94. url.query_pairs_mut().append_pair("clusterMaximum", &x.to_string());
  95. }
  96. if let Some(x) = &params.date_minimum {
  97. url.query_pairs_mut().append_pair("dateMinimum", x);
  98. }
  99. if let Some(x) = &params.date_maximum {
  100. url.query_pairs_mut().append_pair("dateMaximum", x);
  101. }
  102. if let Some(x) = &params.creator_id {
  103. url.query_pairs_mut().append_pair("creatorId", x);
  104. }
  105. if let Some(x) = &params.page {
  106. url.query_pairs_mut().append_pair("page", &x.to_string());
  107. }
  108. if let Some(x) = &params.count {
  109. url.query_pairs_mut().append_pair("count", &x.to_string());
  110. }
  111. url.query_pairs_mut().append_pair("sortBy", &params.sort_by);
  112. url.query_pairs_mut().append_pair("orderBy", &params.order_by);
  113. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  114. let result = self.client.get(url)
  115. .header("Authorization", "Bearer ".to_owned() + &token)
  116. .send().await
  117. .map_err(FactoryError::Protocol)?;
  118. handle_json_response::<SearchResponse>(result).await
  119. }
  120. pub async fn create_robot(&self, robot: CreateRobotPayload) -> Result<CreateRobotResponse, FactoryError> {
  121. let url = Url::parse(FACTORY_DOMAIN)
  122. .unwrap()
  123. .join("/v1/foundry/garage")
  124. .unwrap();
  125. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  126. let result = self.client.post(url)
  127. .header("Authorization", "Bearer ".to_owned() + &token)
  128. .header("Content-Type", "application/json")
  129. .json(&robot)
  130. .send().await
  131. .map_err(FactoryError::Protocol)?;
  132. handle_json_response::<CreateRobotResponse>(result).await
  133. }
  134. pub async fn publish_robot(&self, robot: PublishRobotPayload, id: String) -> Result<PublishRobotResponse, FactoryError> {
  135. let url = Url::parse(FACTORY_DOMAIN)
  136. .unwrap()
  137. .join(&format!("/v1/foundry/vehicles/{}/publish", id))
  138. .unwrap();
  139. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  140. let result = self.client.post(url)
  141. .header("Authorization", "Bearer ".to_owned() + &token)
  142. .header("Content-Type", "application/json")
  143. .json(&robot)
  144. .send().await
  145. .map_err(FactoryError::Protocol)?;
  146. handle_json_response(result).await
  147. }
  148. pub async fn unpublish_bot(&self, id: String) -> Result<(), FactoryError> {
  149. let url = Url::parse(FACTORY_DOMAIN)
  150. .unwrap()
  151. .join(&format!("/v1/foundry/vehicles/{}/unpublish", id))
  152. .unwrap();
  153. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  154. let result = self.client.post(url)
  155. .header("Authorization", "Bearer ".to_owned() + &token)
  156. .send().await
  157. .map_err(FactoryError::Protocol)?;
  158. let status_code = result.status().as_u16();
  159. if status_code > 199 && status_code < 300 {
  160. Ok(())
  161. } else {
  162. match result.json::<ErrorPayload>().await {
  163. Ok(err) => Err(FactoryError::Response(err)),
  164. Err(e) => Err(FactoryError::ResponseCode(e, status_code))
  165. }
  166. }
  167. }
  168. pub async fn delete_robot(&self, id: String) -> Result<(), FactoryError> {
  169. let url = Url::parse(FACTORY_DOMAIN)
  170. .unwrap()
  171. .join(&format!("/v1/foundry/vehicles/{}", id))
  172. .unwrap();
  173. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  174. let result = self.client.delete(url)
  175. .header("Authorization", "Bearer ".to_owned() + &token)
  176. .header("Content-Type", "application/json")
  177. .send().await
  178. .map_err(FactoryError::Protocol)?;
  179. let status_code = result.status().as_u16();
  180. if status_code > 199 && status_code < 300 {
  181. Ok(())
  182. } else {
  183. match result.json::<ErrorPayload>().await {
  184. Ok(err) => Err(FactoryError::Response(err)),
  185. Err(e) => Err(FactoryError::ResponseCode(e, status_code))
  186. }
  187. }
  188. }
  189. pub async fn factory_info(&self) -> Result<FactoryInfoResponse, FactoryError> {
  190. let url = Url::parse(FACTORY_DOMAIN)
  191. .unwrap()
  192. .join("/v1/foundry/info")
  193. .unwrap();
  194. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  195. let result = self.client.get(url)
  196. .header("Authorization", "Bearer ".to_owned() + &token)
  197. .send().await
  198. .map_err(FactoryError::Protocol)?;
  199. handle_json_response::<FactoryInfoResponse>(result).await
  200. }
  201. pub async fn my_robots(&self) -> Result<MyRobotsResponse, FactoryError> {
  202. let url = Url::parse(FACTORY_DOMAIN)
  203. .unwrap()
  204. .join("/v1/foundry/garage")
  205. .unwrap();
  206. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  207. let result = self.client.get(url)
  208. .header("Authorization", "Bearer ".to_owned() + &token)
  209. .send().await
  210. .map_err(FactoryError::Protocol)?;
  211. handle_json_response::<MyRobotsResponse>(result).await
  212. }
  213. pub async fn my_published_robots(&self) -> Result<MyRobotsResponse, FactoryError> {
  214. let url = Url::parse(FACTORY_DOMAIN)
  215. .unwrap()
  216. .join("/v1/foundry/published")
  217. .unwrap();
  218. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  219. let result = self.client.get(url)
  220. .header("Authorization", "Bearer ".to_owned() + &token)
  221. .send().await
  222. .map_err(FactoryError::Protocol)?;
  223. handle_json_response::<MyRobotsResponse>(result).await
  224. }
  225. pub async fn get(&self, id: String) -> Result<GetRobotResponse, FactoryError> {
  226. let url = Url::parse(FACTORY_DOMAIN)
  227. .unwrap()
  228. .join(&format!("/v1/foundry/vehicles/{}", id))
  229. .unwrap();
  230. let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?;
  231. let result = self.client.get(url)
  232. .header("Authorization", "Bearer ".to_owned() + &token)
  233. .send().await
  234. .map_err(FactoryError::Protocol)?;
  235. handle_json_response::<GetRobotResponse>(result).await
  236. }
  237. }