use crate::Driver;
use std::error::Error;
use std::future::Future;
pub trait DriverConnector: Send + Sync {
type Driver: Driver;
type Target: Sync;
type Authentication: Send + 'static;
type Error: Error + Send + Sync + 'static;
fn connect<'a>(
&'a self,
target: &'a Self::Target,
authentication: Self::Authentication,
) -> impl Future<Output = Result<Self::Driver, Self::Error>> + Send + 'a;
}
#[cfg(test)]
mod tests {
use super::DriverConnector;
use crate::{
AdministrativeState, Driver, Execute, InterfaceName, Run, SetInterfaceState, operation,
};
use std::convert::Infallible;
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, Waker};
fn block_on<F: Future>(future: F) -> F::Output {
let mut future = pin!(future);
let mut context = Context::from_waker(Waker::noop());
match future.as_mut().poll(&mut context) {
Poll::Ready(output) => output,
Poll::Pending => panic!("test future unexpectedly yielded"),
}
}
struct DeviceTarget(&'static str);
struct DeviceAuthentication {
username: String,
password: String,
}
struct DeviceDriver {
endpoint: String,
authenticated_user: String,
}
impl Driver for DeviceDriver {
type Error = Infallible;
}
struct TestConnector;
impl DriverConnector for TestConnector {
type Driver = DeviceDriver;
type Target = DeviceTarget;
type Authentication = DeviceAuthentication;
type Error = Infallible;
#[allow(clippy::manual_async_fn)]
fn connect<'a>(
&'a self,
target: &'a Self::Target,
authentication: Self::Authentication,
) -> impl Future<Output = Result<Self::Driver, Self::Error>> + Send + 'a {
async move {
let DeviceAuthentication { username, password } = authentication;
assert_eq!(password, "consumed-secret");
Ok(DeviceDriver {
endpoint: target.0.to_owned(),
authenticated_user: username,
})
}
}
}
impl Execute<SetInterfaceState> for DeviceDriver {
type Output = String;
#[allow(clippy::manual_async_fn)]
fn execute<'a>(
&'a self,
operation: SetInterfaceState,
) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + 'a {
async move {
let state = match operation.state {
AdministrativeState::Enabled => "enabled",
AdministrativeState::Disabled => "disabled",
};
Ok(format!(
"{}:{}:{}:{}",
self.endpoint, self.authenticated_user, operation.interface, state
))
}
}
}
#[test]
fn connector_consumes_authentication_and_created_driver_runs_operation() {
let connector = TestConnector;
let target = DeviceTarget("device.example");
let authentication = DeviceAuthentication {
username: String::from("operator"),
password: String::from("consumed-secret"),
};
let driver = block_on(connector.connect(&target, authentication)).unwrap();
let interface = InterfaceName::try_from("Ethernet1/1").unwrap();
let result = block_on(
operation(SetInterfaceState::new(
interface,
AdministrativeState::Enabled,
))
.run(&driver),
)
.unwrap();
assert_eq!(result, "device.example:operator:Ethernet1/1:enabled");
}
}