use std::convert::TryFrom;
use crate::admin::messages::{self, is_valid_service_id};
use crate::error::InvalidStateError;
use crate::protos::admin;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProposedService {
service_id: String,
service_type: String,
node_id: String,
arguments: Vec<(String, String)>,
}
impl ProposedService {
pub fn service_id(&self) -> &str {
&self.service_id
}
pub fn service_type(&self) -> &str {
&self.service_type
}
pub fn node_id(&self) -> &str {
&self.node_id
}
pub fn arguments(&self) -> &[(String, String)] {
&self.arguments
}
pub fn into_proto(self) -> admin::SplinterService {
let mut proto = admin::SplinterService::new();
proto.set_service_id(self.service_id);
proto.set_service_type(self.service_type);
proto.set_allowed_nodes(protobuf::RepeatedField::from_vec(vec![self.node_id]));
proto.set_arguments(protobuf::RepeatedField::from_vec(
self.arguments
.into_iter()
.map(|(k, v)| {
let mut argument = admin::SplinterService_Argument::new();
argument.set_key(k);
argument.set_value(v);
argument
})
.collect(),
));
proto
}
pub fn from_proto(mut proto: admin::SplinterService) -> Result<Self, InvalidStateError> {
Ok(Self {
service_id: proto.take_service_id(),
service_type: proto.take_service_type(),
node_id: proto
.take_allowed_nodes()
.get(0)
.ok_or_else(|| {
InvalidStateError::with_message(
"unable to build, missing field: `node_id`".to_string(),
)
})?
.to_string(),
arguments: proto
.take_arguments()
.into_iter()
.map(|mut argument| (argument.take_key(), argument.take_value()))
.collect(),
})
}
}
#[derive(Default, Clone)]
pub struct ProposedServiceBuilder {
service_id: Option<String>,
service_type: Option<String>,
node_id: Option<String>,
arguments: Option<Vec<(String, String)>>,
}
impl ProposedServiceBuilder {
pub fn new() -> Self {
ProposedServiceBuilder::default()
}
pub fn service_id(&self) -> Option<String> {
self.service_id.clone()
}
pub fn service_type(&self) -> Option<String> {
self.service_type.clone()
}
pub fn node_id(&self) -> Option<String> {
self.node_id.clone()
}
pub fn arguments(&self) -> Option<Vec<(String, String)>> {
self.arguments.clone()
}
pub fn with_service_id(mut self, service_id: &str) -> ProposedServiceBuilder {
self.service_id = Some(service_id.into());
self
}
pub fn with_service_type(mut self, service_type: &str) -> ProposedServiceBuilder {
self.service_type = Some(service_type.into());
self
}
pub fn with_node_id(mut self, node_id: &str) -> ProposedServiceBuilder {
self.node_id = Some(node_id.into());
self
}
pub fn with_arguments(mut self, arguments: &[(String, String)]) -> ProposedServiceBuilder {
self.arguments = Some(arguments.to_vec());
self
}
pub fn build(self) -> Result<ProposedService, InvalidStateError> {
let service_id = match self.service_id {
Some(service_id) if is_valid_service_id(&service_id) => service_id,
Some(service_id) => {
return Err(InvalidStateError::with_message(format!(
"service_id is invalid ({}): must be a 4 character base62 string",
service_id,
)))
}
None => {
return Err(InvalidStateError::with_message(
"unable to build, missing field: `service_id`".to_string(),
))
}
};
let service_type = self.service_type.ok_or_else(|| {
InvalidStateError::with_message(
"unable to build, missing field: `service_type`".to_string(),
)
})?;
let node_id = self.node_id.ok_or_else(|| {
InvalidStateError::with_message("unable to build, missing field: `node_id`".to_string())
})?;
let arguments = self.arguments.unwrap_or_default();
let service = ProposedService {
service_id,
service_type,
node_id,
arguments,
};
Ok(service)
}
}
impl TryFrom<&messages::SplinterService> for ProposedService {
type Error = InvalidStateError;
fn try_from(
splinter_service: &messages::SplinterService,
) -> Result<ProposedService, Self::Error> {
ProposedServiceBuilder::new()
.with_service_id(&splinter_service.service_id)
.with_service_type(&splinter_service.service_type)
.with_node_id(splinter_service.allowed_nodes.get(0).ok_or_else(|| {
InvalidStateError::with_message("Must contain 1 node ID".to_string())
})?)
.with_arguments(&splinter_service.arguments)
.build()
}
}