libuptest 0.1.4

Core Substrate runtime upgrade Library for UpTest
extern crate alloc;
use alloc::{string::String, vec, vec::Vec};
use serde::Serialize;
use serde_json::{Result, Value};

#[derive(Debug)]
pub struct RpcParams(ParamsBuilder);

pub type ExampleHash = [u8; 32];

impl RpcParams {
    /// Construct a new [`RpcParams`].
    pub fn new() -> Self {
        Self::default()
    }

    /// Insert a plain value into the builder.
    pub fn insert<P: Serialize>(&mut self, value: P) -> Result<()> {
        self.0.insert(value)
    }

    /// Insert a plain value into the builder.
    // Same functionality as `insert` but with the drawback of an extra heap allocation.
    // But it is available in no_std.
    pub fn insert_with_allocation<P: Serialize>(&mut self, value: P) -> Result<()> {
        self.0.insert_with_allocation(value)
    }

    /// Finish the building process and return a JSON compatible string.
    pub fn build(self) -> Option<String> {
        self.0.build()
    }

    pub fn to_json_value(self) -> Result<Value> {
        let params = match self.build() {
            Some(string) => serde_json::from_str(&string)?,
            None => serde_json::json!(vec![Value::Null]),
        };
        Ok(params)
    }
}

impl Default for RpcParams {
    fn default() -> Self {
        Self(ParamsBuilder::positional())
    }
}
/// Initial number of bytes for a parameter length.
const PARAM_BYTES_CAPACITY: usize = 128;

/// Generic parameter builder that serializes parameters to bytes.
/// This produces a JSON compatible String.
///
/// The implementation relies on `Vec<u8>` to hold the serialized
/// parameters in memory for the following reasons:
///   1. Other serialization methods than `serde_json::to_writer` would internally
///      have an extra heap allocation for temporarily holding the value in memory.
///   2. `io::Write` is not implemented for `String` required for serialization.
#[derive(Debug)]
pub struct ParamsBuilder {
    bytes: Vec<u8>,
    start: char,
    end: char,
}

impl ParamsBuilder {
    /// Construct a new [`ParamsBuilder`] with custom start and end tokens.
    /// The inserted values are wrapped by the _start_ and _end_ tokens.
    fn new(start: char, end: char) -> Self {
        ParamsBuilder {
            bytes: Vec::new(),
            start,
            end,
        }
    }

    /// Construct a new [`ParamsBuilder`] for positional parameters equivalent to a JSON array object.
    pub(crate) fn positional() -> Self {
        Self::new('[', ']')
    }

    /// Initialize the internal vector if it is empty:
    ///  - allocate [`PARAM_BYTES_CAPACITY`] to avoid resizing
    ///  - add the `start` character.
    ///
    /// # Note
    ///
    /// Initialization is needed prior to inserting elements.
    fn maybe_initialize(&mut self) {
        if self.bytes.is_empty() {
            self.bytes.reserve(PARAM_BYTES_CAPACITY);
            self.bytes.push(self.start as u8);
        }
    }

    /// Finish the building process and return a JSON compatible string.
    pub(crate) fn build(mut self) -> Option<String> {
        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);
        }

        // Safety: This is safe because JSON does not emit invalid UTF-8.
        Some(unsafe { String::from_utf8_unchecked(self.bytes) })
    }

    /// Insert a plain value into the builder without heap allocation.
    #[cfg(feature = "std")]
    pub(crate) fn insert<P: Serialize>(&mut self, value: P) -> Result<()> {
        self.maybe_initialize();

        serde_json::to_writer(&mut self.bytes, &value)?;
        self.bytes.push(b',');

        Ok(())
    }

    /// Insert a plain value into the builder with heap allocation. If available,
    /// use the more efficient std version.
    #[cfg(not(feature = "std"))]
    pub(crate) fn insert<P: Serialize>(&mut self, value: P) -> Result<()> {
        self.insert_with_allocation(value)
    }

    /// Insert a plain value into the builder with heap allocation. For better performance,
    /// use the std version, if possible.
    pub(crate) fn insert_with_allocation<P: Serialize>(&mut self, value: P) -> Result<()> {
        self.maybe_initialize();

        let mut serialized_vec = serde_json::to_vec(&value)?;
        self.bytes.append(&mut serialized_vec);
        self.bytes.push(b',');

        Ok(())
    }
}

// this function was written by parity
#[macro_export]
macro_rules! rpc_params {
	($($param:expr),*) => {
		{

			let mut params = RpcParams::new();
			$(
				if let Err(err) = params.insert($param) {
					panic!("Parameter `{}` cannot be serialized: {:?}", stringify!($param), err);
				}
			)*
			params
		}
	};
}