corepc_client/client_sync/
mod.rsmod error;
pub mod v17;
pub mod v18;
pub mod v19;
pub mod v20;
pub mod v21;
pub mod v22;
pub mod v23;
pub mod v24;
pub mod v25;
pub mod v26;
pub mod v27;
pub mod v28;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
pub use crate::client_sync::error::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Auth {
None,
UserPass(String, String),
CookieFile(PathBuf),
}
impl Auth {
pub fn get_user_pass(self) -> Result<(Option<String>, Option<String>)> {
match self {
Auth::None => Ok((None, None)),
Auth::UserPass(u, p) => Ok((Some(u), Some(p))),
Auth::CookieFile(path) => {
let line = BufReader::new(File::open(path)?)
.lines()
.next()
.ok_or(Error::InvalidCookieFile)??;
let colon = line.find(':').ok_or(Error::InvalidCookieFile)?;
Ok((Some(line[..colon].into()), Some(line[colon + 1..].into())))
}
}
}
}
#[macro_export]
macro_rules! define_jsonrpc_minreq_client {
($version:literal) => {
use std::fmt;
use $crate::client_sync::{log_response, Auth, Result};
use $crate::client_sync::error::Error;
pub struct Client {
inner: jsonrpc::client::Client,
}
impl fmt::Debug for Client {
fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
write!(
f,
"corepc_client::client_sync::{}::Client({:?})", $version, self.inner
)
}
}
impl Client {
pub fn new(url: &str) -> Self {
let transport = jsonrpc::http::minreq_http::Builder::new()
.url(url)
.expect("jsonrpc v0.18, this function does not error")
.build();
let inner = jsonrpc::client::Client::with_transport(transport);
Self { inner }
}
pub fn new_with_auth(url: &str, auth: Auth) -> Result<Self> {
if matches!(auth, Auth::None) {
return Err(Error::MissingUserPassword);
}
let (user, pass) = auth.get_user_pass()?;
let transport = jsonrpc::http::minreq_http::Builder::new()
.url(url)
.expect("jsonrpc v0.18, this function does not error")
.basic_auth(user.unwrap(), pass)
.build();
let inner = jsonrpc::client::Client::with_transport(transport);
Ok(Self { inner })
}
pub fn call<T: for<'a> serde::de::Deserialize<'a>>(
&self,
method: &str,
args: &[serde_json::Value],
) -> Result<T> {
let raw = serde_json::value::to_raw_value(args)?;
let req = self.inner.build_request(&method, Some(&*raw));
if log::log_enabled!(log::Level::Debug) {
log::debug!(target: "corepc", "request: {} {}", method, serde_json::Value::from(args));
}
let resp = self.inner.send_request(req).map_err(Error::from);
log_response(method, &resp);
Ok(resp?.result()?)
}
}
}
}
#[macro_export]
macro_rules! impl_client_check_expected_server_version {
($expected_versions:expr) => {
impl Client {
pub fn check_expected_server_version(&self) -> Result<()> {
let server_version = self.server_version()?;
if !$expected_versions.contains(&server_version) {
return Err($crate::client_sync::error::UnexpectedServerVersionError {
got: server_version,
expected: $expected_versions.to_vec(),
})?;
}
Ok(())
}
}
};
}
fn into_json<T>(val: T) -> Result<serde_json::Value>
where
T: serde::ser::Serialize,
{
Ok(serde_json::to_value(val)?)
}
#[allow(dead_code)] fn opt_into_json<T>(opt: Option<T>) -> Result<serde_json::Value>
where
T: serde::ser::Serialize,
{
match opt {
Some(val) => Ok(into_json(val)?),
None => Ok(serde_json::Value::Null),
}
}
#[allow(dead_code)] fn null() -> serde_json::Value { serde_json::Value::Null }
#[allow(dead_code)] fn empty_arr() -> serde_json::Value { serde_json::Value::Array(vec![]) }
#[allow(dead_code)] fn empty_obj() -> serde_json::Value { serde_json::Value::Object(Default::default()) }
#[allow(dead_code)] fn handle_defaults<'a>(
args: &'a mut [serde_json::Value],
defaults: &[serde_json::Value],
) -> &'a [serde_json::Value] {
assert!(args.len() >= defaults.len());
let mut first_non_null_optional_idx = None;
for i in 0..defaults.len() {
let args_i = args.len() - 1 - i;
let defaults_i = defaults.len() - 1 - i;
if args[args_i] == serde_json::Value::Null {
if first_non_null_optional_idx.is_some() {
if defaults[defaults_i] == serde_json::Value::Null {
panic!("Missing `default` for argument idx {}", args_i);
}
args[args_i] = defaults[defaults_i].clone();
}
} else if first_non_null_optional_idx.is_none() {
first_non_null_optional_idx = Some(args_i);
}
}
let required_num = args.len() - defaults.len();
if let Some(i) = first_non_null_optional_idx {
&args[..i + 1]
} else {
&args[..required_num]
}
}
#[allow(dead_code)] fn opt_result<T: for<'a> serde::de::Deserialize<'a>>(
result: serde_json::Value,
) -> Result<Option<T>> {
if result == serde_json::Value::Null {
Ok(None)
} else {
Ok(serde_json::from_value(result)?)
}
}
fn log_response(method: &str, resp: &Result<jsonrpc::Response>) {
use log::Level::{Debug, Trace, Warn};
if log::log_enabled!(Warn) || log::log_enabled!(Debug) || log::log_enabled!(Trace) {
match resp {
Err(ref e) =>
if log::log_enabled!(Debug) {
log::debug!(target: "corepc", "error: {}: {:?}", method, e);
},
Ok(ref resp) =>
if let Some(ref e) = resp.error {
if log::log_enabled!(Debug) {
log::debug!(target: "corepc", "response error for {}: {:?}", method, e);
}
} else if log::log_enabled!(Trace) {
let def =
serde_json::value::to_raw_value(&serde_json::value::Value::Null).unwrap();
let result = resp.result.as_ref().unwrap_or(&def);
log::trace!(target: "corepc", "response for {}: {}", method, result);
},
}
}
}