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};
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<T: DeserializeOwned>(
+ &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<Instance, ClientError> {
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)
}
} 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::<Account>(req)?;
if id.is_some_and(|id| ac.id != id) {
return Err(ClientError::UrlConsistency(
url,
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::<Poll>(req)?;
if poll.id != id {
return Err(ClientError::UrlConsistency(
url,
&mut self,
id: &str,
) -> Result<Relationship, ClientError> {
- let (url, rsp) = self.api_request(
- Req::get("api/v1/accounts/relationships").param("id", id),
- )?;
- let rspstatus = rsp.status();
- let rels: Vec<Relationship> = 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::<Vec<Relationship>>(req)?;
for rel in rels {
if rel.id == id {
return Ok(rel);
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::<Status>(req)?;
if st.id != id {
return Err(ClientError::UrlConsistency(
url,
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::<Notification>(req)?;
if not.id != id {
return Err(ClientError::UrlConsistency(
url,
&mut self,
name: &str,
) -> Result<Account, ClientError> {
- 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::<Account>(req)?.0;
self.cache_account(&ac);
Ok(ac)
}
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::<Status>(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(())
}
&mut self,
id: &str,
) -> Result<Context, ClientError> {
- 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::<Context>(req)?.0;
for st in &ctx.ancestors {
self.cache_status(st);
}
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::<Poll>(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(())
}
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::<Account>(req)?;
if ac.id != id {
return Err(ClientError::UrlConsistency(
url,
.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::<Application>(req)?.0;
+ Ok(app)
}
pub fn get_app_token(
.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::<Token>(req)?.0;
+ Ok(tok)
}
pub fn verify_app_credentials(
&mut self,
) -> Result<Application, ClientError> {
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::<Application>(req)?.0;
+ Ok(app)
}
pub fn get_auth_url(
.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::<Token>(req)?.0;
+ Ok(tok)
}
pub fn register_confirmation(