use core::convert::{TryFrom, TryInto};
use std::prelude::v1::*;
use super::{ConnectorArgs, ConnectorInstanceArcBox, Inventory, OsArgs, OsInstanceArcBox};
use crate::error::{Error, ErrorKind, ErrorOrigin, Result};
pub enum BuildStep<'a> {
Connector {
name: &'a str,
args: Option<ConnectorArgs>,
},
Os {
name: &'a str,
args: Option<OsArgs>,
},
}
impl<'a> BuildStep<'a> {
pub fn new_connector(input: &'a str) -> Result<Self> {
let (name, args) = input.split_once(':').unwrap_or((input, ""));
Ok(Self::Connector {
name,
args: if args.is_empty() {
None
} else {
Some(str::parse(args)?)
},
})
}
pub fn new_os(input: &'a str) -> Result<Self> {
let (name, args) = input.split_once(':').unwrap_or((input, ""));
Ok(Self::Os {
name,
args: if args.is_empty() {
None
} else {
Some(str::parse(args)?)
},
})
}
pub fn validate_next(&self, next: &Self) -> bool {
!matches!(
(self, next),
(BuildStep::Connector { .. }, BuildStep::Connector { .. })
| (BuildStep::Os { .. }, BuildStep::Os { .. })
)
}
}
fn builder_from_args<'a>(
connectors: impl Iterator<Item = (usize, &'a str)>,
os_layers: impl Iterator<Item = (usize, &'a str)>,
) -> Result<Vec<BuildStep<'a>>> {
let mut layers = connectors
.map(|(i, a)| BuildStep::new_connector(a).map(|a| (i, a)))
.chain(os_layers.map(|(i, a)| BuildStep::new_os(a).map(|a| (i, a))))
.collect::<Result<Vec<_>>>()?;
layers.sort_by(|(a, _), (b, _)| a.cmp(b));
if layers.windows(2).any(|w| !w[0].1.validate_next(&w[1].1)) {
return Err(
Error(ErrorOrigin::Other, ErrorKind::ArgValidation).log_error(
"invalid builder configuration, build steps cannot be used in the given order",
),
);
}
Ok(layers.into_iter().map(|(_, s)| s).collect())
}
pub struct ConnectorChain<'a>(Vec<BuildStep<'a>>);
impl<'a> ConnectorChain<'a> {
pub fn new(
connectors: impl Iterator<Item = (usize, &'a str)>,
os_layers: impl Iterator<Item = (usize, &'a str)>,
) -> Result<Self> {
let steps = builder_from_args(connectors, os_layers)?;
steps.try_into()
}
}
impl<'a> TryFrom<Vec<BuildStep<'a>>> for ConnectorChain<'a> {
type Error = Error;
fn try_from(steps: Vec<BuildStep<'a>>) -> Result<Self> {
if !matches!(steps.last(), Some(BuildStep::Connector { .. })) {
return Err(
Error(ErrorOrigin::Other, ErrorKind::ArgValidation).log_error(
"invalid builder configuration, last build step has to be a connector",
),
);
}
Ok(Self(steps))
}
}
pub struct OsChain<'a>(Vec<BuildStep<'a>>);
impl<'a> OsChain<'a> {
pub fn new(
connectors: impl Iterator<Item = (usize, &'a str)>,
os_layers: impl Iterator<Item = (usize, &'a str)>,
) -> Result<Self> {
let steps = builder_from_args(connectors, os_layers)?;
steps.try_into()
}
}
impl<'a> TryFrom<Vec<BuildStep<'a>>> for OsChain<'a> {
type Error = Error;
fn try_from(steps: Vec<BuildStep<'a>>) -> Result<Self> {
if !matches!(steps.last(), Some(BuildStep::Os { .. })) {
return Err(Error(ErrorOrigin::Other, ErrorKind::ArgValidation)
.log_error("invalid builder configuration, last build step has to be a os"));
}
Ok(Self(steps))
}
}
pub struct BuilderEmpty<'a> {
inventory: &'a mut Inventory,
}
impl<'a> BuilderEmpty<'a> {
pub fn new(inventory: &'a mut Inventory) -> Self {
Self { inventory }
}
pub fn connector(self, name: &'a str) -> OsBuilder<'a> {
OsBuilder {
inventory: self.inventory,
steps: vec![BuildStep::Connector { name, args: None }],
}
}
pub fn os(self, name: &'a str) -> ConnectorBuilder<'a> {
ConnectorBuilder {
inventory: self.inventory,
steps: vec![BuildStep::Os { name, args: None }],
}
}
pub fn os_chain(self, chain: OsChain<'a>) -> ConnectorBuilder<'a> {
ConnectorBuilder {
inventory: self.inventory,
steps: chain.0,
}
}
pub fn connector_chain(self, chain: ConnectorChain<'a>) -> OsBuilder<'a> {
OsBuilder {
inventory: self.inventory,
steps: chain.0,
}
}
}
pub struct ConnectorBuilder<'a> {
inventory: &'a mut Inventory,
steps: Vec<BuildStep<'a>>,
}
impl<'a> ConnectorBuilder<'a> {
pub fn connector(self, name: &'a str) -> OsBuilder<'a> {
let mut steps = self.steps;
steps.push(BuildStep::Connector { name, args: None });
OsBuilder {
inventory: self.inventory,
steps,
}
}
pub fn args(mut self, os_args: OsArgs) -> ConnectorBuilder<'a> {
if let Some(BuildStep::Os { name: _, args }) = self.steps.iter_mut().last() {
*args = Some(os_args);
}
self
}
pub fn build(self) -> Result<OsInstanceArcBox<'static>> {
let mut connector: Option<ConnectorInstanceArcBox<'static>> = None;
let mut os: Option<OsInstanceArcBox<'static>> = None;
for step in self.steps.iter() {
match step {
BuildStep::Connector { name, args } => {
connector = Some(self.inventory.instantiate_connector(
name,
os,
args.as_ref(),
)?);
os = None;
}
BuildStep::Os { name, args } => {
os = Some(
self.inventory
.instantiate_os(name, connector, args.as_ref())?,
);
connector = None;
}
};
}
os.ok_or(Error(ErrorOrigin::Inventory, ErrorKind::Configuration))
}
}
pub struct OsBuilder<'a> {
inventory: &'a mut Inventory,
steps: Vec<BuildStep<'a>>,
}
impl<'a> OsBuilder<'a> {
pub fn os(self, name: &'a str) -> ConnectorBuilder<'a> {
let mut steps = self.steps;
steps.push(BuildStep::Os { name, args: None });
ConnectorBuilder {
inventory: self.inventory,
steps,
}
}
pub fn args(mut self, conn_args: ConnectorArgs) -> OsBuilder<'a> {
if let Some(BuildStep::Connector { name: _, args }) = self.steps.iter_mut().last() {
*args = Some(conn_args);
}
self
}
pub fn build(self) -> Result<ConnectorInstanceArcBox<'static>> {
let mut connector: Option<ConnectorInstanceArcBox<'static>> = None;
let mut os: Option<OsInstanceArcBox<'static>> = None;
for step in self.steps.iter() {
match step {
BuildStep::Connector { name, args } => {
connector = Some(self.inventory.instantiate_connector(
name,
os,
args.as_ref(),
)?);
os = None;
}
BuildStep::Os { name, args } => {
os = Some(
self.inventory
.instantiate_os(name, connector, args.as_ref())?,
);
connector = None;
}
};
}
connector.ok_or(Error(ErrorOrigin::Inventory, ErrorKind::Configuration))
}
}