From: Ian Jackson Date: Fri, 2 Feb 2024 14:19:36 +0000 (+0000) Subject: client: Introduce api_request_parse and use it in 14 places X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=38d6f7720c7fd8d6f64468a4638c776e9358f2e8;p=mastodonochrome.git client: Introduce api_request_parse and use it in 14 places --- diff --git a/src/client.rs b/src/client.rs index e85008e..b8d66cd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,5 +1,6 @@ use chrono::{DateTime, Utc}; use reqwest::Url; +use serde::de::DeserializeOwned; use std::collections::{HashMap, HashSet, VecDeque}; use std::fs::File; use std::io::{IoSlice, Read, Write}; @@ -657,21 +658,32 @@ impl Client { Ok((urlstr, rsp)) } + /// Makes a request and deserialises the returned JSON + /// + /// Insists that the response is a success. + /// Returns the `T` and the URL. + fn api_request_parse( + &mut self, + req: Req, + ) -> Result<(T, String), ClientError> { + let (url, rsp) = self.api_request(req)?; + let rspstatus = rsp.status(); + if !rspstatus.is_success() { + return Err(ClientError::from_response(&url, rsp)); + } + let t: T = serde_json::from_str(&rsp.text()?) + .map_err(|e| ClientError::JSONParse(url.clone(), e.to_string()))?; + Ok((t, url)) + } + pub fn instance(&mut self) -> Result { if let Some(ref inst) = self.instance { return Ok(inst.clone()); } - let (url, rsp) = self.api_request(Req::get("api/v2/instance"))?; - let rspstatus = rsp.status(); - let inst: Instance = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(ac) => Ok(ac), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - } - }?; + let inst: Instance = + self.api_request_parse(Req::get("api/v2/instance"))?.0; + self.instance = Some(inst.clone()); Ok(inst) } @@ -738,18 +750,7 @@ impl Client { } else { Req::get(&format!("api/v1/accounts/{}", id.unwrap())) }; - let (url, rsp) = self.api_request(req)?; - let rspstatus = rsp.status(); - let ac: Account = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(ac) => Ok(ac), - Err(e) => { - Err(ClientError::JSONParse(url.clone(), e.to_string())) - } - } - }?; + let (ac, url) = self.api_request_parse::(req)?; if id.is_some_and(|id| ac.id != id) { return Err(ClientError::UrlConsistency( url, @@ -775,19 +776,8 @@ impl Client { return Ok(st.clone()); } - let (url, rsp) = - self.api_request(Req::get(&("api/v1/polls/".to_owned() + id)))?; - let rspstatus = rsp.status(); - let poll: Poll = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(poll) => Ok(poll), - Err(e) => { - Err(ClientError::JSONParse(url.clone(), e.to_string())) - } - } - }?; + let req = Req::get(&("api/v1/polls/".to_owned() + id)); + let (poll, url) = self.api_request_parse::(req)?; if poll.id != id { return Err(ClientError::UrlConsistency( url, @@ -802,20 +792,8 @@ impl Client { &mut self, id: &str, ) -> Result { - let (url, rsp) = self.api_request( - Req::get("api/v1/accounts/relationships").param("id", id), - )?; - let rspstatus = rsp.status(); - let rels: Vec = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(ac) => Ok(ac), - Err(e) => { - Err(ClientError::JSONParse(url.clone(), e.to_string())) - } - } - }?; + let req = Req::get("api/v1/accounts/relationships").param("id", id); + let (rels, url) = self.api_request_parse::>(req)?; for rel in rels { if rel.id == id { return Ok(rel); @@ -850,19 +828,8 @@ impl Client { return Ok(st); } - let (url, rsp) = - self.api_request(Req::get(&("api/v1/statuses/".to_owned() + id)))?; - let rspstatus = rsp.status(); - let st: Status = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(st) => Ok(st), - Err(e) => { - Err(ClientError::JSONParse(url.clone(), e.to_string())) - } - } - }?; + let req = Req::get(&("api/v1/statuses/".to_owned() + id)); + let (st, url) = self.api_request_parse::(req)?; if st.id != id { return Err(ClientError::UrlConsistency( url, @@ -893,20 +860,8 @@ impl Client { return Ok(not); } - let (url, rsp) = self.api_request(Req::get( - &("api/v1/notifications/".to_owned() + id), - ))?; - let rspstatus = rsp.status(); - let not: Notification = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(st) => Ok(st), - Err(e) => { - Err(ClientError::JSONParse(url.clone(), e.to_string())) - } - } - }?; + let req = Req::get(&("api/v1/notifications/".to_owned() + id)); + let (not, url) = self.api_request_parse::(req)?; if not.id != id { return Err(ClientError::UrlConsistency( url, @@ -1379,18 +1334,8 @@ impl Client { &mut self, name: &str, ) -> Result { - let (url, rsp) = self.api_request( - Req::get("api/v1/accounts/lookup").param("acct", name), - )?; - let rspstatus = rsp.status(); - let ac: Account = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(ac) => Ok(ac), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - } - }?; + let req = Req::get("api/v1/accounts/lookup").param("acct", name); + let ac = self.api_request_parse::(req)?.0; self.cache_account(&ac); Ok(ac) } @@ -1426,18 +1371,9 @@ impl Client { id: &str, verb: &str, ) -> Result<(), ClientError> { - let (url, rsp) = self - .api_request(Req::post(&format!("api/v1/statuses/{id}/{verb}")))?; - let rspstatus = rsp.status(); + let req = Req::post(&format!("api/v1/statuses/{id}/{verb}")); + let st = self.api_request_parse::(req)?.0; // Cache the returned status so as to update its faved/boosted flags - let st: Status = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(st) => Ok(st), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - } - }?; self.cache_status(&st); Ok(()) } @@ -1464,17 +1400,8 @@ impl Client { &mut self, id: &str, ) -> Result { - let (url, rsp) = self - .api_request(Req::get(&format!("api/v1/statuses/{id}/context")))?; - let rspstatus = rsp.status(); - let ctx: Context = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(st) => Ok(st), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - } - }?; + let req = Req::get(&format!("api/v1/statuses/{id}/context")); + let ctx = self.api_request_parse::(req)?.0; for st in &ctx.ancestors { self.cache_status(st); } @@ -1494,17 +1421,8 @@ impl Client { for choice in choices { req = req.param("choices[]", choice); } - let (url, rsp) = self.api_request(req)?; - let rspstatus = rsp.status(); + let poll = self.api_request_parse::(req)?.0; // Cache the returned poll so as to update its faved/boosted flags - let poll: Poll = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(poll) => Ok(poll), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - } - }?; self.cache_poll(&poll); Ok(()) } @@ -1588,18 +1506,7 @@ impl Client { req }; - let (url, rsp) = self.api_request(req)?; - let rspstatus = rsp.status(); - let ac: Account = if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - match serde_json::from_str(&rsp.text()?) { - Ok(ac) => Ok(ac), - Err(e) => { - Err(ClientError::JSONParse(url.clone(), e.to_string())) - } - } - }?; + let (ac, url) = self.api_request_parse::(req)?; if ac.id != id { return Err(ClientError::UrlConsistency( url, @@ -1641,17 +1548,8 @@ impl Client { .param("client_name", "Mastodonochrome") .param("scopes", "read write push") .param("website", WEBSITE); - let (url, rsp) = self.api_request(req)?; - let rspstatus = rsp.status(); - if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - let app: Application = match serde_json::from_str(&rsp.text()?) { - Ok(app) => Ok(app), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - }?; - Ok(app) - } + let app = self.api_request_parse::(req)?.0; + Ok(app) } pub fn get_app_token( @@ -1691,34 +1589,16 @@ impl Client { .param("code", code) .param("scope", "read write push"), }; - let (url, rsp) = self.api_request(req)?; - let rspstatus = rsp.status(); - if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - let tok: Token = match serde_json::from_str(&rsp.text()?) { - Ok(tok) => Ok(tok), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - }?; - Ok(tok) - } + let tok = self.api_request_parse::(req)?.0; + Ok(tok) } pub fn verify_app_credentials( &mut self, ) -> Result { let req = Req::get("api/v1/apps/verify_credentials"); - let (url, rsp) = self.api_request(req)?; - let rspstatus = rsp.status(); - if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - let app: Application = match serde_json::from_str(&rsp.text()?) { - Ok(app) => Ok(app), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - }?; - Ok(app) - } + let app = self.api_request_parse::(req)?.0; + Ok(app) } pub fn get_auth_url( @@ -1755,17 +1635,8 @@ impl Client { .param("password", password) .param("agreement", true) // the calling UI should have confirmed .param("locale", language); - let (url, rsp) = self.api_request(req)?; - let rspstatus = rsp.status(); - if !rspstatus.is_success() { - Err(ClientError::from_response(&url, rsp)) - } else { - let tok: Token = match serde_json::from_str(&rsp.text()?) { - Ok(tok) => Ok(tok), - Err(e) => Err(ClientError::JSONParse(url, e.to_string())), - }?; - Ok(tok) - } + let tok = self.api_request_parse::(req)?.0; + Ok(tok) } pub fn register_confirmation(