1- // shadowharvester/ src/api.rs
1+ // src/api.rs
22
3- use serde:: { Deserialize , Serialize } ;
4- use reqwest;
53use reqwest:: blocking;
6- use serde_json;
74
8- // FIX: Import MOCK constants from the new module
9- use crate :: constants:: USER_AGENT ;
5+ // FIX: Import structs from the new module location
6+ use crate :: data_types:: {
7+ TandCResponse , RegistrationReceipt , ChallengeData , ChallengeResponse ,
8+ SolutionReceipt , DonateResponse , Statistics , StatisticsApiResponse , CliChallengeData , ApiErrorResponse
9+ } ;
1010
11- // --- RESPONSE STRUCTS ---
12-
13- #[ derive( Debug , Deserialize ) ]
14- pub struct TandCResponse {
15- pub version : String ,
16- pub content : String ,
17- pub message : String ,
18- }
19-
20- #[ derive( Debug , Deserialize ) ]
21- pub struct RegistrationReceipt {
22- #[ serde( rename = "registrationReceipt" ) ]
23- pub registration_receipt : serde_json:: Value ,
24- }
25-
26- #[ derive( Debug , Deserialize , Serialize , Clone ) ]
27- pub struct ChallengeData {
28- pub challenge_id : String ,
29- pub difficulty : String ,
30- #[ serde( rename = "no_pre_mine" ) ]
31- pub no_pre_mine_key : String ,
32- #[ serde( rename = "no_pre_mine_hour" ) ]
33- pub no_pre_mine_hour_str : String ,
34- pub latest_submission : String ,
35- // NEW: Fields for listing command
36- pub challenge_number : u16 ,
37- pub day : u8 ,
38- pub issued_at : String ,
39- }
40-
41- #[ derive( Debug , Deserialize ) ]
42- pub struct ChallengeResponse { // Made struct public for use in main.rs
43- pub code : String ,
44- pub challenge : Option < ChallengeData > ,
45- pub starts_at : Option < String > ,
46- // NEW: Fields for listing command (overall status)
47- pub mining_period_ends : Option < String > ,
48- pub max_day : Option < u8 > ,
49- pub total_challenges : Option < u16 > ,
50- pub current_day : Option < u8 > ,
51- pub next_challenge_starts_at : Option < String > ,
52- }
53-
54- #[ derive( Debug , Deserialize , PartialEq ) ]
55- pub struct SolutionReceipt {
56- #[ serde( rename = "crypto_receipt" ) ]
57- pub crypto_receipt : serde_json:: Value ,
58- }
59-
60- #[ derive( Debug , Deserialize , PartialEq ) ]
61- pub struct DonateResponse {
62- pub status : String ,
63- #[ serde( rename = "donation_id" ) ]
64- pub donation_id : String ,
65- }
66-
67-
68- #[ derive( Debug , Deserialize ) ]
69- struct ApiErrorResponse {
70- pub message : String ,
71- pub error : Option < String > ,
72- pub statusCode : Option < u16 > ,
73- }
11+ // --- API FUNCTIONS ---
7412
75- #[ derive( Debug , Deserialize ) ]
76- pub struct GlobalStatistics {
77- pub wallets : u32 ,
78- pub challenges : u16 ,
79- #[ serde( rename = "total_challenges" ) ]
80- pub total_challenges : u16 ,
81- #[ serde( rename = "total_crypto_receipts" ) ]
82- pub total_crypto_receipts : u32 ,
83- #[ serde( rename = "recent_crypto_receipts" ) ]
84- pub recent_crypto_receipts : u32 ,
85- }
13+ /// Fetches the T&C from the API, returning the full response object.
14+ pub fn fetch_tandc ( client : & blocking:: Client , api_url : & str ) -> Result < TandCResponse , reqwest:: Error > {
15+ let url = format ! ( "{}/TandC/1-0" , api_url) ;
16+ println ! ( "-> Fetching Terms and Conditions from: {}" , url) ;
8617
87- // NEW: Struct for the statistics under the "local" key
88- #[ derive( Debug , Deserialize ) ]
89- pub struct LocalStatistics {
90- pub crypto_receipts : u32 ,
91- pub night_allocation : u32 ,
92- }
18+ let response = client. get ( url) . send ( ) ?;
9319
94- // NEW: Struct representing the entire JSON response from the /statistics/:address endpoint
95- #[ derive( Debug , Deserialize ) ]
96- pub struct StatisticsApiResponse {
97- pub global : GlobalStatistics ,
98- pub local : LocalStatistics ,
99- }
20+ let response = response. error_for_status ( ) ?;
10021
101- #[ derive( Debug ) ]
102- pub struct Statistics {
103- // Local Address (Added by the client)
104- pub local_address : String ,
105- // Global fields
106- pub wallets : u32 ,
107- pub challenges : u16 ,
108- pub total_challenges : u16 ,
109- pub total_crypto_receipts : u32 ,
110- pub recent_crypto_receipts : u32 ,
111- // Local fields
112- pub crypto_receipts : u32 ,
113- pub night_allocation : u32 ,
114- }
115- // NEW: Struct for the challenge parameters provided via CLI
116- #[ derive( Debug , Clone ) ]
117- pub struct CliChallengeData {
118- pub challenge_id : String ,
119- pub no_pre_mine_key : String ,
120- pub difficulty : String ,
121- pub no_pre_mine_hour_str : String ,
122- pub latest_submission : String ,
22+ response. json ( )
12323}
12424
12525// NEW FUNCTION: Parses the comma-separated CLI challenge string
@@ -143,20 +43,6 @@ pub fn parse_cli_challenge_string(challenge_str: &str) -> Result<CliChallengeDat
14343}
14444
14545
146- // --- API FUNCTIONS ---
147-
148- /// Fetches the T&C from the API, returning the full response object.
149- pub fn fetch_tandc ( client : & blocking:: Client , api_url : & str ) -> Result < TandCResponse , reqwest:: Error > {
150- let url = format ! ( "{}/TandC/1-0" , api_url) ;
151- println ! ( "-> Fetching Terms and Conditions from: {}" , url) ;
152-
153- let response = client. get ( url) . send ( ) ?;
154-
155- let response = response. error_for_status ( ) ?;
156-
157- response. json ( )
158- }
159-
16046/// Performs the POST /register call using key/signature arguments.
16147// RENAMED from register_address_mock
16248pub fn register_address (
@@ -191,34 +77,17 @@ pub fn register_address(
19177 Ok ( ( ) )
19278}
19379
194- /// Fetches the current challenge parameters from the API.
195- pub fn fetch_challenge ( client : & blocking:: Client , api_url : & str ) -> Result < ChallengeData , String > {
196- let url = format ! ( "{}/challenge" , api_url) ;
197- println ! ( "-> Fetching current challenge from: {}" , url) ;
198-
199- let response = client. get ( url) . send ( ) . map_err ( |e| format ! ( "API request failed: {}" , e) ) ?;
80+ /// Helper to format a detailed error message from the API response body.
81+ fn format_detailed_api_error ( err : ApiErrorResponse , status : reqwest:: StatusCode ) -> String {
82+ let mut msg = format ! ( "(Status {}) {}" , status. as_u16( ) , err. message) ;
20083
201- if !response . status ( ) . is_success ( ) {
202- return Err ( format ! ( "Challenge API returned non-success status : {}" , response . status ( ) ) ) ;
84+ if let Some ( e ) = err . error {
85+ msg . push_str ( & format ! ( " [Type : {}] " , e ) ) ;
20386 }
204-
205- let challenge_response: ChallengeResponse = response. json ( ) . map_err ( |e| format ! ( "JSON parsing failed: {}" , e) ) ?;
206-
207- match challenge_response. code . as_str ( ) {
208- "active" => {
209- Ok ( challenge_response. challenge . unwrap ( ) )
210- }
211- "before" => {
212- let start_time = challenge_response. starts_at . unwrap_or_default ( ) ;
213- Err ( format ! ( "MINING IS NOT YET ACTIVE. Starts at: {}" , start_time) )
214- }
215- "after" => {
216- Err ( "MINING PERIOD HAS ENDED." . to_string ( ) )
217- }
218- _ => {
219- Err ( format ! ( "Received unexpected challenge code: {}" , challenge_response. code) )
220- }
87+ if let Some ( code) = err. status_code {
88+ msg. push_str ( & format ! ( " [API Code: {}]" , code) ) ;
22189 }
90+ msg
22291}
22392
22493/// Performs the POST /solution call.
@@ -258,8 +127,8 @@ pub fn submit_solution(
258127
259128 match api_error {
260129 Ok ( err) => {
261- // API returned a structured error message (e.g., Not registered, Invalid difficulty)
262- Err ( format ! ( "API Validation Failed (Status {}) : {}" , status . as_u16 ( ) , err . message ) )
130+ // FIX: Use all error fields for detailed reporting
131+ Err ( format ! ( "API Validation Failed: {}" , format_detailed_api_error ( err , status ) ) )
263132 }
264133 Err ( _) => {
265134 // API returned a non-structured error (e.g., plain text or unreadable JSON)
@@ -288,7 +157,6 @@ pub fn donate_to(
288157 ) ;
289158
290159 println ! ( "-> Donating funds from {} to {}" , original_address, destination_address) ;
291- println ! ( "url: {}" , & url) ;
292160
293161 let response = client
294162 . post ( & url)
@@ -308,7 +176,8 @@ pub fn donate_to(
308176
309177 match api_error {
310178 Ok ( err) => {
311- Err ( format ! ( "Donation Failed (Status {}): {}" , status. as_u16( ) , err. message) )
179+ // FIX: Use all error fields for detailed reporting
180+ Err ( format ! ( "Donation Failed: {}" , format_detailed_api_error( err, status) ) )
312181 }
313182 Err ( _) => {
314183 Err ( format ! ( "HTTP Error {} with unparseable body: {}" , status. as_u16( ) , body_text) )
@@ -335,7 +204,7 @@ pub fn fetch_challenge_status(client: &blocking::Client, api_url: &str) -> Resul
335204/// Fetches and validates the active challenge parameters, returning data only if active.
336205// RENAMED from fetch_challenge
337206pub fn get_active_challenge_data ( client : & blocking:: Client , api_url : & str ) -> Result < ChallengeData , String > {
338- let challenge_response = fetch_challenge_status ( & client, api_url) ?;
207+ let challenge_response = fetch_challenge_status ( client, api_url) ?;
339208
340209 match challenge_response. code . as_str ( ) {
341210 "active" => {
@@ -378,19 +247,19 @@ pub fn fetch_statistics(client: &blocking::Client, api_url: &str, address: &str)
378247 wallets : api_data. global . wallets ,
379248 challenges : api_data. global . challenges ,
380249 total_challenges : api_data. global . total_challenges ,
381- total_crypto_receipts : api_data. global . total_crypto_receipts ,
382250 recent_crypto_receipts : api_data. global . recent_crypto_receipts ,
251+ total_crypto_receipts : api_data. global . total_crypto_receipts ,
383252 crypto_receipts : api_data. local . crypto_receipts ,
384253 night_allocation : api_data. local . night_allocation ,
385254 } )
386255 } else {
387256 let body_text = response. text ( ) . unwrap_or_else ( |_| format ! ( "(Could not read response body for status {})" , status) ) ;
388- // ... (error handling omitted)
389257 let api_error: Result < ApiErrorResponse , _ > = serde_json:: from_str ( & body_text) ;
390258
391259 match api_error {
392260 Ok ( err) => {
393- Err ( format ! ( "API Error (Status {}): {}" , status. as_u16( ) , err. message) )
261+ // FIX: Use all error fields for detailed reporting
262+ Err ( format ! ( "API Error: {}" , format_detailed_api_error( err, status) ) )
394263 }
395264 Err ( _) => {
396265 Err ( format ! ( "HTTP Error {} with unparseable body: {}" , status. as_u16( ) , body_text) )
0 commit comments