use std::future::Future;
use serde_json::Value;
use crate::types::ChiaQueryError;
pub trait HttpTransport {
fn post_json(
&self,
url: String,
body: Value,
) -> impl Future<Output = Result<Value, ChiaQueryError>>;
}
#[cfg(feature = "native")]
mod native {
use std::time::Duration;
use serde_json::Value;
use super::HttpTransport;
use crate::types::ChiaQueryError;
#[derive(Clone)]
pub struct ReqwestTransport {
http: reqwest::Client,
}
impl ReqwestTransport {
pub fn new(timeout: Duration) -> Result<Self, ChiaQueryError> {
let http = reqwest::Client::builder()
.timeout(timeout)
.build()
.map_err(|e| ChiaQueryError::CoinsetHttp(e.to_string()))?;
Ok(Self { http })
}
}
impl HttpTransport for ReqwestTransport {
async fn post_json(&self, url: String, body: Value) -> Result<Value, ChiaQueryError> {
let resp = self
.http
.post(&url)
.json(&body)
.send()
.await
.map_err(|e| ChiaQueryError::CoinsetHttp(e.to_string()))?;
resp.json()
.await
.map_err(|e| ChiaQueryError::CoinsetHttp(e.to_string()))
}
}
}
#[cfg(feature = "native")]
pub use native::ReqwestTransport;
#[cfg(all(target_arch = "wasm32", feature = "coinset", not(feature = "native")))]
mod wasm {
use serde_json::Value;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use super::HttpTransport;
use crate::types::ChiaQueryError;
pub struct FetchTransport {
fetch: js_sys::Function,
}
impl FetchTransport {
pub fn new(fetch: js_sys::Function) -> Self {
Self { fetch }
}
fn err(context: &str, value: &JsValue) -> ChiaQueryError {
ChiaQueryError::CoinsetHttp(format!(
"{context}: {}",
value.as_string().unwrap_or_else(|| "js error".into())
))
}
}
impl HttpTransport for FetchTransport {
async fn post_json(&self, url: String, body: Value) -> Result<Value, ChiaQueryError> {
let init = js_sys::Object::new();
js_sys::Reflect::set(&init, &"method".into(), &"POST".into())
.map_err(|e| Self::err("set method", &e))?;
let headers = js_sys::Object::new();
js_sys::Reflect::set(&headers, &"Content-Type".into(), &"application/json".into())
.map_err(|e| Self::err("set header", &e))?;
js_sys::Reflect::set(&init, &"headers".into(), &headers)
.map_err(|e| Self::err("set headers", &e))?;
let payload = serde_json::to_string(&body)
.map_err(|e| ChiaQueryError::CoinsetHttp(e.to_string()))?;
js_sys::Reflect::set(&init, &"body".into(), &JsValue::from_str(&payload))
.map_err(|e| Self::err("set body", &e))?;
let promise = self
.fetch
.call2(&JsValue::NULL, &JsValue::from_str(&url), &init)
.map_err(|e| Self::err("call fetch", &e))?;
let response = JsFuture::from(js_sys::Promise::from(promise))
.await
.map_err(|e| Self::err("await fetch", &e))?;
let json_fn = js_sys::Reflect::get(&response, &"json".into())
.map_err(|e| Self::err("get json()", &e))?
.dyn_into::<js_sys::Function>()
.map_err(|e| Self::err("json not callable", &e))?;
let json_promise = json_fn
.call0(&response)
.map_err(|e| Self::err("call json()", &e))?;
let value = JsFuture::from(js_sys::Promise::from(json_promise))
.await
.map_err(|e| Self::err("await json()", &e))?;
serde_wasm_bindgen::from_value(value)
.map_err(|e| ChiaQueryError::CoinsetHttp(format!("parse json: {e}")))
}
}
}
#[cfg(all(target_arch = "wasm32", feature = "coinset", not(feature = "native")))]
pub use wasm::FetchTransport;