game_manager_lib/services/integration/pcgamingwiki/
client.rs1use crate::constants::{PCGW_API_BASE, REQUEST_PCGW_DELAY_MS};
7use crate::errors::AppError;
8use reqwest::Client;
9use serde_json::Value;
10use std::time::Duration;
11use tokio::time::sleep;
12
13const PCGW_USER_AGENT: &str = "Playlite/1.0 (playlite.app.dev@gmail.com)";
15
16#[derive(Debug, serde::Serialize, serde::Deserialize)]
20pub struct PcgwSearchResult {
21 #[serde(rename = "pageId")]
22 pub page_id: String,
23 #[serde(rename = "pageName")]
24 pub page_name: String,
25}
26
27pub(crate) fn build_http_client() -> Result<Client, AppError> {
31 Client::builder()
32 .user_agent(PCGW_USER_AGENT)
33 .timeout(Duration::from_secs(15))
34 .build()
35 .map_err(|e| AppError::NetworkError(e.to_string()))
36}
37
38pub(crate) async fn cargo_query(
42 client: &Client,
43 tables: &str,
44 fields: &str,
45 where_clause: &str,
46 limit: u32,
47) -> Result<Vec<Value>, AppError> {
48 sleep(Duration::from_millis(REQUEST_PCGW_DELAY_MS)).await;
49
50 let response = client
51 .get(PCGW_API_BASE)
52 .query(&[
53 ("action", "cargoquery"),
54 ("tables", tables),
55 ("fields", fields),
56 ("where", where_clause),
57 ("limit", &limit.to_string()),
58 ("format", "json"),
59 ])
60 .send()
61 .await
62 .map_err(|e| AppError::NetworkError(e.to_string()))?;
63
64 if !response.status().is_success() {
65 let status = response.status();
66 return Err(AppError::NetworkError(format!(
67 "PCGW API retornou HTTP {}",
68 status
69 )));
70 }
71
72 let json: Value = response
73 .json()
74 .await
75 .map_err(|e| AppError::ParseError(e.to_string()))?;
76
77 let rows = json
79 .get("cargoquery")
80 .and_then(|v| v.as_array())
81 .cloned()
82 .unwrap_or_default();
83
84 Ok(rows)
85}
86
87pub async fn search_pcgw_by_name(game_name: &str) -> Result<Vec<PcgwSearchResult>, AppError> {
93 let client = build_http_client()?;
94
95 sleep(Duration::from_millis(REQUEST_PCGW_DELAY_MS)).await;
96
97 let response = client
98 .get(PCGW_API_BASE)
99 .query(&[
100 ("action", "query"),
101 ("list", "search"),
102 ("srsearch", game_name),
103 ("srnamespace", "0"),
104 ("srlimit", "5"),
105 ("format", "json"),
106 ])
107 .send()
108 .await
109 .map_err(|e| AppError::NetworkError(e.to_string()))?;
110
111 let json: Value = response
112 .json()
113 .await
114 .map_err(|e| AppError::ParseError(e.to_string()))?;
115
116 let results = json
117 .pointer("/query/search")
118 .and_then(|v| v.as_array())
119 .map(|hits| {
120 hits.iter()
121 .filter_map(|hit| {
122 Some(PcgwSearchResult {
123 page_id: hit.get("pageid")?.as_u64()?.to_string(),
124 page_name: hit.get("title")?.as_str()?.to_string(),
125 })
126 })
127 .collect()
128 })
129 .unwrap_or_default();
130
131 Ok(results)
132}