use std::convert::TryFrom;
use crate::admin::messages::{self, is_valid_circuit_id};
use crate::error::InvalidStateError;
use crate::protos::admin;
use super::{
AuthorizationType, CircuitStatus, DurabilityType, PersistenceType, ProposedNode,
ProposedService, RouteType, UNSET_CIRCUIT_VERSION,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProposedCircuit {
circuit_id: String,
roster: Vec<ProposedService>,
members: Vec<ProposedNode>,
authorization_type: AuthorizationType,
persistence: PersistenceType,
durability: DurabilityType,
routes: RouteType,
circuit_management_type: String,
application_metadata: Option<Vec<u8>>,
comments: Option<String>,
display_name: Option<String>,
circuit_version: i32,
circuit_status: CircuitStatus,
}
impl ProposedCircuit {
pub fn circuit_id(&self) -> &str {
&self.circuit_id
}
pub fn roster(&self) -> &[ProposedService] {
&self.roster
}
pub fn members(&self) -> &[ProposedNode] {
&self.members
}
pub fn authorization_type(&self) -> &AuthorizationType {
&self.authorization_type
}
pub fn persistence(&self) -> &PersistenceType {
&self.persistence
}
pub fn durability(&self) -> &DurabilityType {
&self.durability
}
pub fn routes(&self) -> &RouteType {
&self.routes
}
pub fn circuit_management_type(&self) -> &str {
&self.circuit_management_type
}
pub fn application_metadata(&self) -> &Option<Vec<u8>> {
&self.application_metadata
}
pub fn comments(&self) -> &Option<String> {
&self.comments
}
pub fn display_name(&self) -> &Option<String> {
&self.display_name
}
pub fn circuit_version(&self) -> i32 {
self.circuit_version
}
pub fn circuit_status(&self) -> &CircuitStatus {
&self.circuit_status
}
pub fn from_proto(mut proto: admin::Circuit) -> Result<Self, InvalidStateError> {
let authorization_type = match proto.get_authorization_type() {
admin::Circuit_AuthorizationType::TRUST_AUTHORIZATION => AuthorizationType::Trust,
admin::Circuit_AuthorizationType::CHALLENGE_AUTHORIZATION => {
AuthorizationType::Challenge
}
_ => {
return Err(InvalidStateError::with_message(
"unable to build, missing or unsupported field: `authorization_type`"
.to_string(),
));
}
};
let persistence = match proto.get_persistence() {
admin::Circuit_PersistenceType::ANY_PERSISTENCE => PersistenceType::Any,
admin::Circuit_PersistenceType::UNSET_PERSISTENCE_TYPE => {
return Err(InvalidStateError::with_message(
"unable to build, missing field: `persistence type`".to_string(),
));
}
};
let durability = match proto.get_durability() {
admin::Circuit_DurabilityType::NO_DURABILITY => DurabilityType::NoDurability,
admin::Circuit_DurabilityType::UNSET_DURABILITY_TYPE => {
return Err(InvalidStateError::with_message(
"unable to build, missing field: `durability type`".to_string(),
));
}
};
let routes = match proto.get_routes() {
admin::Circuit_RouteType::ANY_ROUTE => RouteType::Any,
admin::Circuit_RouteType::UNSET_ROUTE_TYPE => {
return Err(InvalidStateError::with_message(
"unable to build, missing field: `route type`".to_string(),
));
}
};
let circuit_status = match proto.get_circuit_status() {
admin::Circuit_CircuitStatus::ACTIVE => CircuitStatus::Active,
admin::Circuit_CircuitStatus::DISBANDED => CircuitStatus::Disbanded,
admin::Circuit_CircuitStatus::ABANDONED => CircuitStatus::Abandoned,
admin::Circuit_CircuitStatus::UNSET_CIRCUIT_STATUS => {
debug!("Defaulting `UNSET_CIRCUIT_STATUS` of proposed circuit to `Active`");
CircuitStatus::default()
}
};
let display_name = if proto.get_display_name().is_empty() {
None
} else {
Some(proto.take_display_name())
};
let comments = if proto.get_comments().is_empty() {
None
} else {
Some(proto.take_comments())
};
let application_metadata = if proto.get_application_metadata().is_empty() {
None
} else {
Some(proto.take_application_metadata())
};
let circuit_version = if proto.get_circuit_version() == 0 {
UNSET_CIRCUIT_VERSION
} else {
proto.get_circuit_version()
};
Ok(Self {
circuit_id: proto.take_circuit_id(),
roster: proto
.take_roster()
.into_iter()
.map(ProposedService::from_proto)
.collect::<Result<Vec<ProposedService>, InvalidStateError>>()?,
members: proto
.take_members()
.into_iter()
.map(ProposedNode::from_proto)
.collect::<Vec<ProposedNode>>(),
authorization_type,
persistence,
durability,
routes,
circuit_management_type: proto.take_circuit_management_type(),
application_metadata,
comments,
display_name,
circuit_version,
circuit_status,
})
}
pub fn into_proto(self) -> admin::Circuit {
let mut circuit = admin::Circuit::new();
circuit.set_circuit_id(self.circuit_id);
circuit.set_roster(protobuf::RepeatedField::from_vec(
self.roster
.into_iter()
.map(ProposedService::into_proto)
.collect(),
));
circuit.set_members(protobuf::RepeatedField::from_vec(
self.members
.into_iter()
.map(ProposedNode::into_proto)
.collect(),
));
circuit.set_circuit_management_type(self.circuit_management_type);
if let Some(application_metadata) = self.application_metadata {
circuit.set_application_metadata(application_metadata);
}
if let Some(comments) = self.comments {
circuit.set_comments(comments);
}
if let Some(display_name) = self.display_name {
circuit.set_display_name(display_name);
}
if self.circuit_version != UNSET_CIRCUIT_VERSION {
circuit.set_circuit_version(self.circuit_version);
}
match self.authorization_type {
AuthorizationType::Trust => {
circuit
.set_authorization_type(admin::Circuit_AuthorizationType::TRUST_AUTHORIZATION);
}
AuthorizationType::Challenge => {
circuit.set_authorization_type(
admin::Circuit_AuthorizationType::CHALLENGE_AUTHORIZATION,
);
}
};
match self.persistence {
PersistenceType::Any => {
circuit.set_persistence(admin::Circuit_PersistenceType::ANY_PERSISTENCE);
}
};
match self.durability {
DurabilityType::NoDurability => {
circuit.set_durability(admin::Circuit_DurabilityType::NO_DURABILITY);
}
};
match self.routes {
RouteType::Any => circuit.set_routes(admin::Circuit_RouteType::ANY_ROUTE),
};
if self.circuit_version > UNSET_CIRCUIT_VERSION {
match self.circuit_status {
CircuitStatus::Active => {
circuit.set_circuit_status(admin::Circuit_CircuitStatus::ACTIVE);
}
CircuitStatus::Disbanded => {
circuit.set_circuit_status(admin::Circuit_CircuitStatus::DISBANDED);
}
CircuitStatus::Abandoned => {
circuit.set_circuit_status(admin::Circuit_CircuitStatus::ABANDONED);
}
}
}
circuit
}
}
#[derive(Default, Clone)]
pub struct ProposedCircuitBuilder {
circuit_id: Option<String>,
roster: Option<Vec<ProposedService>>,
members: Option<Vec<ProposedNode>>,
authorization_type: Option<AuthorizationType>,
persistence: Option<PersistenceType>,
durability: Option<DurabilityType>,
routes: Option<RouteType>,
circuit_management_type: Option<String>,
application_metadata: Option<Vec<u8>>,
comments: Option<String>,
display_name: Option<String>,
circuit_version: Option<i32>,
circuit_status: Option<CircuitStatus>,
}
impl ProposedCircuitBuilder {
pub fn new() -> Self {
ProposedCircuitBuilder::default()
}
pub fn circuit_id(&self) -> Option<String> {
self.circuit_id.clone()
}
pub fn roster(&self) -> Option<Vec<ProposedService>> {
self.roster.clone()
}
pub fn members(&self) -> Option<Vec<ProposedNode>> {
self.members.clone()
}
pub fn authorization_type(&self) -> Option<AuthorizationType> {
self.authorization_type.clone()
}
pub fn persistence(&self) -> Option<PersistenceType> {
self.persistence.clone()
}
pub fn durability(&self) -> Option<DurabilityType> {
self.durability.clone()
}
pub fn routes(&self) -> Option<RouteType> {
self.routes.clone()
}
pub fn circuit_management_type(&self) -> Option<String> {
self.circuit_management_type.clone()
}
pub fn application_metadata(&self) -> Option<Vec<u8>> {
self.application_metadata.clone()
}
pub fn comments(&self) -> Option<String> {
self.comments.clone()
}
pub fn display_name(&self) -> Option<String> {
self.display_name.clone()
}
pub fn circuit_version(&self) -> Option<i32> {
self.circuit_version
}
pub fn circuit_status(&self) -> Option<CircuitStatus> {
self.circuit_status.clone()
}
pub fn with_circuit_id(mut self, circuit_id: &str) -> ProposedCircuitBuilder {
self.circuit_id = Some(circuit_id.into());
self
}
pub fn with_roster(mut self, services: &[ProposedService]) -> ProposedCircuitBuilder {
self.roster = Some(services.into());
self
}
pub fn with_members(mut self, members: &[ProposedNode]) -> ProposedCircuitBuilder {
self.members = Some(members.into());
self
}
pub fn with_authorization_type(mut self, auth: &AuthorizationType) -> ProposedCircuitBuilder {
self.authorization_type = Some(auth.clone());
self
}
pub fn with_persistence(mut self, persistence: &PersistenceType) -> ProposedCircuitBuilder {
self.persistence = Some(persistence.clone());
self
}
pub fn with_durability(mut self, durability: &DurabilityType) -> ProposedCircuitBuilder {
self.durability = Some(durability.clone());
self
}
pub fn with_routes(mut self, route_type: &RouteType) -> ProposedCircuitBuilder {
self.routes = Some(route_type.clone());
self
}
pub fn with_circuit_management_type(
mut self,
circuit_management_type: &str,
) -> ProposedCircuitBuilder {
self.circuit_management_type = Some(circuit_management_type.into());
self
}
pub fn with_application_metadata(
mut self,
application_metadata: &[u8],
) -> ProposedCircuitBuilder {
self.application_metadata = Some(application_metadata.into());
self
}
pub fn with_comments(mut self, comments: &str) -> ProposedCircuitBuilder {
self.comments = Some(comments.into());
self
}
pub fn with_display_name(mut self, display_name: &str) -> ProposedCircuitBuilder {
self.display_name = Some(display_name.into());
self
}
pub fn with_circuit_version(mut self, circuit_version: i32) -> ProposedCircuitBuilder {
self.circuit_version = Some(circuit_version);
self
}
pub fn with_circuit_status(mut self, circuit_status: &CircuitStatus) -> ProposedCircuitBuilder {
self.circuit_status = Some(circuit_status.clone());
self
}
pub fn build(self) -> Result<ProposedCircuit, InvalidStateError> {
let circuit_id = match self.circuit_id {
Some(circuit_id) if is_valid_circuit_id(&circuit_id) => circuit_id,
Some(circuit_id) => {
return Err(InvalidStateError::with_message(format!(
"circuit_id is invalid ({}): must be an 11 character string composed of two, \
5 character base62 strings joined with a '-' (example: abcDE-F0123)",
circuit_id,
)))
}
None => {
return Err(InvalidStateError::with_message(
"unable to build, missing field: `circuit_id`".to_string(),
))
}
};
let roster = self.roster.ok_or_else(|| {
InvalidStateError::with_message("unable to build, missing field: `roster`".to_string())
})?;
let members = self.members.ok_or_else(|| {
InvalidStateError::with_message("unable to build, missing field: `members`".to_string())
})?;
let authorization_type = self.authorization_type.unwrap_or(AuthorizationType::Trust);
let persistence = self.persistence.unwrap_or_default();
let durability = self.durability.unwrap_or(DurabilityType::NoDurability);
let routes = self.routes.unwrap_or_default();
let circuit_management_type = self.circuit_management_type.ok_or_else(|| {
InvalidStateError::with_message(
"unable to build, missing field: `circuit_management_type`".to_string(),
)
})?;
let application_metadata = self.application_metadata;
let comments = self.comments;
let display_name = self.display_name;
let circuit_version = self.circuit_version.unwrap_or(UNSET_CIRCUIT_VERSION);
let circuit_status = self.circuit_status.unwrap_or_default();
let create_circuit_message = ProposedCircuit {
circuit_id,
roster,
members,
authorization_type,
persistence,
durability,
routes,
circuit_management_type,
application_metadata,
comments,
display_name,
circuit_version,
circuit_status,
};
Ok(create_circuit_message)
}
}
impl TryFrom<&messages::CreateCircuit> for ProposedCircuit {
type Error = InvalidStateError;
fn try_from(create_circuit: &messages::CreateCircuit) -> Result<ProposedCircuit, Self::Error> {
let services = create_circuit
.roster
.iter()
.map(ProposedService::try_from)
.collect::<Result<Vec<ProposedService>, InvalidStateError>>()?;
let nodes = create_circuit
.members
.iter()
.map(ProposedNode::from)
.collect::<Vec<ProposedNode>>();
let mut circuit_builder = ProposedCircuitBuilder::new()
.with_circuit_id(&create_circuit.circuit_id)
.with_roster(&services)
.with_members(&nodes)
.with_authorization_type(&AuthorizationType::from(&create_circuit.authorization_type))
.with_persistence(&PersistenceType::from(&create_circuit.persistence))
.with_durability(&DurabilityType::from(&create_circuit.durability))
.with_routes(&RouteType::from(&create_circuit.routes))
.with_circuit_management_type(&create_circuit.circuit_management_type)
.with_circuit_version(create_circuit.circuit_version)
.with_circuit_status(&CircuitStatus::from(&create_circuit.circuit_status));
if !create_circuit.application_metadata.is_empty() {
circuit_builder =
circuit_builder.with_application_metadata(&create_circuit.application_metadata);
}
if let Some(comments) = &create_circuit.comments {
circuit_builder = circuit_builder.with_comments(comments);
}
if let Some(display_name) = &create_circuit.display_name {
circuit_builder = circuit_builder.with_display_name(display_name);
}
circuit_builder.build()
}
}