use crate::traits::ToRpcParams;
use serde::Serialize;
use serde_json::value::RawValue;
mod params_builder {
use serde::Serialize;
use serde_json::value::RawValue;
const PARAM_BYTES_CAPACITY: usize = 128;
#[derive(Debug, Clone)]
pub(crate) struct ParamsBuilder {
bytes: Vec<u8>,
start: char,
end: char,
}
impl ParamsBuilder {
fn new(start: char, end: char) -> Self {
ParamsBuilder { bytes: Vec::new(), start, end }
}
pub(crate) fn positional() -> Self {
Self::new('[', ']')
}
pub(crate) fn named() -> Self {
Self::new('{', '}')
}
fn maybe_initialize(&mut self) {
if self.bytes.is_empty() {
self.bytes.reserve(PARAM_BYTES_CAPACITY);
self.bytes.push(self.start as u8);
}
}
pub(crate) fn insert_named<P: Serialize>(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> {
self.maybe_initialize();
serde_json::to_writer(&mut self.bytes, name)?;
self.bytes.push(b':');
serde_json::to_writer(&mut self.bytes, &value)?;
self.bytes.push(b',');
Ok(())
}
pub(crate) fn insert<P: Serialize>(&mut self, value: P) -> Result<(), serde_json::Error> {
self.maybe_initialize();
serde_json::to_writer(&mut self.bytes, &value)?;
self.bytes.push(b',');
Ok(())
}
pub(crate) fn build(mut self) -> Option<Box<RawValue>> {
if self.bytes.is_empty() {
return None;
}
let idx = self.bytes.len() - 1;
if self.bytes[idx] == b',' {
self.bytes[idx] = self.end as u8;
} else {
self.bytes.push(self.end as u8);
}
let json_str = unsafe { String::from_utf8_unchecked(self.bytes) };
Some(RawValue::from_string(json_str).expect("Valid JSON String; qed"))
}
}
}
#[derive(Debug, Clone)]
pub struct ObjectParams(params_builder::ParamsBuilder);
impl ObjectParams {
pub fn new() -> Self {
Self::default()
}
pub fn insert<P: Serialize>(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> {
self.0.insert_named(name, value)
}
}
impl Default for ObjectParams {
fn default() -> Self {
Self(params_builder::ParamsBuilder::named())
}
}
impl ToRpcParams for ObjectParams {
fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, serde_json::Error> {
Ok(self.0.build())
}
}
#[derive(Debug, Clone)]
pub struct ArrayParams(params_builder::ParamsBuilder);
impl ArrayParams {
pub fn new() -> Self {
Self::default()
}
pub fn insert<P: Serialize>(&mut self, value: P) -> Result<(), serde_json::Error> {
self.0.insert(value)
}
}
impl Default for ArrayParams {
fn default() -> Self {
Self(params_builder::ParamsBuilder::positional())
}
}
impl ToRpcParams for ArrayParams {
fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, serde_json::Error> {
Ok(self.0.build())
}
}
const BATCH_PARAMS_NUM_CAPACITY: usize = 4;
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("Empty batch request is not allowed")]
pub struct EmptyBatchRequest;
#[derive(Clone, Debug, Default)]
pub struct BatchRequestBuilder<'a>(Vec<(&'a str, Option<Box<RawValue>>)>);
impl<'a> BatchRequestBuilder<'a> {
pub fn new() -> Self {
Self(Vec::with_capacity(BATCH_PARAMS_NUM_CAPACITY))
}
pub fn insert<Params: ToRpcParams>(&mut self, method: &'a str, value: Params) -> Result<(), serde_json::Error> {
self.0.push((method, value.to_rpc_params()?));
Ok(())
}
#[allow(clippy::type_complexity)]
pub fn build(self) -> Result<Vec<(&'a str, Option<Box<RawValue>>)>, EmptyBatchRequest> {
if self.0.is_empty() { Err(EmptyBatchRequest) } else { Ok(self.0) }
}
pub fn iter(&self) -> impl Iterator<Item = (&'a str, Option<&RawValue>)> {
self.0.iter().map(|(method, params)| (*method, params.as_deref()))
}
}
impl<'a> IntoIterator for BatchRequestBuilder<'a> {
type Item = (&'a str, Option<Box<RawValue>>);
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}