use soup::prelude::*; pub use kittybox_util::micropub::{Error as MicropubError, Config, QueryType}; #[derive(Debug)] pub struct Client { micropub: String, access_token: String, //http: soup::Session, } #[derive(Debug, thiserror::Error)] pub enum Error { #[error("glib error: {0}")] Glib(#[from] glib::Error), #[error("json serialization error: {0}")] Json(#[from] serde_json::Error), #[error("micropub error: {0}")] Micropub(#[from] MicropubError), #[error("micropub server did not return a location: header")] NoLocationHeader } impl Client { pub fn new(uri: glib::Uri, token: String) -> Self { Self { micropub: uri.to_string(), access_token: token, //http: soup::Session::new() } } pub async fn config(&self) -> Result { let uri = glib::Uri::parse(&self.micropub, glib::UriFlags::NONE).unwrap(); let uri = super::util::append_query( &uri, [("q".to_string(), "config".to_string())] ); let exch = soup::Message::from_uri("GET", &uri); let headers = exch.request_headers().expect("SoupMessage with no headers"); // TODO: create a SoupAuth subclass that allows pasting in a token headers.append("Authorization", &format!("Bearer {}", self.access_token)); let http = soup::Session::new(); let body = http.send_and_read_future(&exch, glib::Priority::DEFAULT).await?; Ok(serde_json::from_slice(&body)?) } pub async fn send_post(&self, post: microformats::types::Item) -> Result { let uri = glib::Uri::parse(&self.micropub, glib::UriFlags::NONE).unwrap(); let exch = soup::Message::from_uri("POST", &uri); let headers = exch.request_headers().expect("SoupMessage with no headers"); headers.append("Authorization", &format!("Bearer {}", self.access_token)); exch.set_request_body_from_bytes(Some("application/json"), Some(&glib::Bytes::from_owned(serde_json::to_vec(&post).unwrap())) ); let http = soup::Session::new(); let body = http.send_and_read_future(&exch, glib::Priority::DEFAULT).await?; match exch.status() { soup::Status::Created | soup::Status::Accepted => { let response_headers = exch.response_headers().expect("Successful SoupMessage with no response headers"); let location = response_headers.one("Location").ok_or(Error::NoLocationHeader)?; Ok(glib::Uri::parse(&location, glib::UriFlags::NONE)?) }, soup::Status::InternalServerError | soup::Status::BadGateway | soup::Status::ServiceUnavailable => { todo!("micropub server is down") }, _ => { let error = serde_json::from_slice::(&body)?; Err(error.into()) } } } }