mod error;
#[cfg(feature = "reqwest")]
mod reqwest;
use std::str::FromStr;
use std::time::Duration;
use transact::protocol::batch::Batch;
pub use self::error::ScabbardClientError;
#[cfg(feature = "reqwest")]
pub use self::reqwest::ReqwestScabbardClient;
#[cfg(feature = "reqwest")]
pub use self::reqwest::ReqwestScabbardClientBuilder;
pub struct ServiceId {
circuit: String,
service_id: String,
}
impl ServiceId {
pub fn new(circuit: &str, service_id: &str) -> Self {
Self {
circuit: circuit.into(),
service_id: service_id.into(),
}
}
pub fn from_string(full_id: &str) -> Result<Self, ScabbardClientError> {
let ids = full_id.splitn(2, "::").collect::<Vec<_>>();
let circuit = (*ids
.get(0)
.ok_or_else(|| ScabbardClientError::new("service ID invalid: cannot be empty"))?)
.to_string();
if circuit.is_empty() {
return Err(ScabbardClientError::new(
"service ID invalid: circuit ID cannot be empty",
));
}
let service_id = (*ids.get(1).ok_or_else(|| {
ScabbardClientError::new(
"service ID invalid: must be of the form 'circuit_id::service_id'",
)
})?)
.to_string();
if service_id.is_empty() {
return Err(ScabbardClientError::new(
"service ID invalid: service ID cannot be empty",
));
}
Ok(Self {
circuit,
service_id,
})
}
pub fn circuit(&self) -> &str {
&self.circuit
}
pub fn service_id(&self) -> &str {
&self.service_id
}
}
impl FromStr for ServiceId {
type Err = ScabbardClientError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_string(s)
}
}
#[derive(Debug, PartialEq)]
pub struct StateEntry {
address: String,
value: Vec<u8>,
}
impl StateEntry {
pub fn address(&self) -> &str {
&self.address
}
pub fn value(&self) -> &[u8] {
&self.value
}
}
pub trait ScabbardClient {
fn submit(
&self,
service_id: &ServiceId,
batches: Vec<Batch>,
wait: Option<Duration>,
) -> Result<(), ScabbardClientError>;
fn get_state_at_address(
&self,
service_id: &ServiceId,
address: &str,
) -> Result<Option<Vec<u8>>, ScabbardClientError>;
fn get_state_with_prefix(
&self,
service_id: &ServiceId,
prefix: Option<&str>,
) -> Result<Vec<StateEntry>, ScabbardClientError>;
fn get_current_state_root(&self, service_id: &ServiceId)
-> Result<String, ScabbardClientError>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn service_id_from_string() {
assert!(ServiceId::from_string("").is_err());
assert!(ServiceId::from_string("01234-abcde").is_err());
assert!(ServiceId::from_string("::").is_err());
assert!(ServiceId::from_string("01234-abcde::").is_err());
assert!(ServiceId::from_string("::ABCD").is_err());
let service_id = ServiceId::from_string("01234-abcde::ABCD").expect("failed to parse");
assert_eq!(service_id.circuit(), "01234-abcde");
assert_eq!(service_id.service_id(), "ABCD");
}
#[test]
fn test_parse_service_id() {
let service_id: ServiceId = "circuit::service".parse().expect("Unable to parse");
assert_eq!("circuit", service_id.circuit());
assert_eq!("service", service_id.service_id());
let parse_res: Result<ServiceId, _> = "circuit::".parse();
assert!(matches!(parse_res, Err(ScabbardClientError { .. })));
let parse_res: Result<ServiceId, _> = "circuit".parse();
assert!(matches!(parse_res, Err(ScabbardClientError { .. })));
let parse_res: Result<ServiceId, _> = "".parse();
assert!(matches!(parse_res, Err(ScabbardClientError { .. })));
}
}