network_device/
connector.rs1use crate::Driver;
4use std::error::Error;
5use std::future::Future;
6
7pub trait DriverConnector: Send + Sync {
53 type Driver: Driver;
55 type Target: Sync;
57 type Authentication: Send + 'static;
59 type Error: Error + Send + Sync + 'static;
61
62 fn connect<'a>(
67 &'a self,
68 target: &'a Self::Target,
69 authentication: Self::Authentication,
70 ) -> impl Future<Output = Result<Self::Driver, Self::Error>> + Send + 'a;
71}
72
73#[cfg(test)]
74mod tests {
75 use super::DriverConnector;
76 use crate::{
77 AdministrativeState, Driver, Execute, InterfaceName, Run, SetInterfaceState, operation,
78 };
79 use std::convert::Infallible;
80 use std::future::Future;
81 use std::pin::pin;
82 use std::task::{Context, Poll, Waker};
83
84 fn block_on<F: Future>(future: F) -> F::Output {
85 let mut future = pin!(future);
86 let mut context = Context::from_waker(Waker::noop());
87 match future.as_mut().poll(&mut context) {
88 Poll::Ready(output) => output,
89 Poll::Pending => panic!("test future unexpectedly yielded"),
90 }
91 }
92
93 struct DeviceTarget(&'static str);
94
95 struct DeviceAuthentication {
96 username: String,
97 password: String,
98 }
99
100 struct DeviceDriver {
101 endpoint: String,
102 authenticated_user: String,
103 }
104
105 impl Driver for DeviceDriver {
106 type Error = Infallible;
107 }
108
109 struct TestConnector;
110
111 impl DriverConnector for TestConnector {
112 type Driver = DeviceDriver;
113 type Target = DeviceTarget;
114 type Authentication = DeviceAuthentication;
115 type Error = Infallible;
116
117 #[allow(clippy::manual_async_fn)]
118 fn connect<'a>(
119 &'a self,
120 target: &'a Self::Target,
121 authentication: Self::Authentication,
122 ) -> impl Future<Output = Result<Self::Driver, Self::Error>> + Send + 'a {
123 async move {
124 let DeviceAuthentication { username, password } = authentication;
125 assert_eq!(password, "consumed-secret");
126 Ok(DeviceDriver {
127 endpoint: target.0.to_owned(),
128 authenticated_user: username,
129 })
130 }
131 }
132 }
133
134 impl Execute<SetInterfaceState> for DeviceDriver {
135 type Output = String;
136
137 #[allow(clippy::manual_async_fn)]
138 fn execute<'a>(
139 &'a self,
140 operation: SetInterfaceState,
141 ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + 'a {
142 async move {
143 let state = match operation.state {
144 AdministrativeState::Enabled => "enabled",
145 AdministrativeState::Disabled => "disabled",
146 };
147 Ok(format!(
148 "{}:{}:{}:{}",
149 self.endpoint, self.authenticated_user, operation.interface, state
150 ))
151 }
152 }
153 }
154
155 #[test]
156 fn connector_consumes_authentication_and_created_driver_runs_operation() {
157 let connector = TestConnector;
158 let target = DeviceTarget("device.example");
159 let authentication = DeviceAuthentication {
160 username: String::from("operator"),
161 password: String::from("consumed-secret"),
162 };
163 let driver = block_on(connector.connect(&target, authentication)).unwrap();
164 let interface = InterfaceName::try_from("Ethernet1/1").unwrap();
165
166 let result = block_on(
167 operation(SetInterfaceState::new(
168 interface,
169 AdministrativeState::Enabled,
170 ))
171 .run(&driver),
172 )
173 .unwrap();
174
175 assert_eq!(result, "device.example:operator:Ethernet1/1:enabled");
176 }
177}