use crate::vpn::profile::WgProfile;
use crate::vpn::{Backend, ConnectionInfo, NetError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Preference {
Auto,
Native,
Docker,
}
pub trait Connector {
fn available(&self) -> bool;
fn connect(&self, profile: &WgProfile) -> Result<ConnectionInfo, NetError>;
fn status(&self) -> Result<Option<ConnectionInfo>, NetError>;
fn disconnect(&self) -> Result<(), NetError>;
}
pub(crate) fn choose(
pref: Preference,
native_ok: bool,
docker_ok: bool,
) -> Result<Backend, NetError> {
match pref {
Preference::Native => {
if native_ok {
Ok(Backend::Native)
} else {
Err(NetError::NoBackend)
}
}
Preference::Docker => {
if docker_ok {
Ok(Backend::Docker)
} else {
Err(NetError::NoBackend)
}
}
Preference::Auto => {
if native_ok {
Ok(Backend::Native)
} else if docker_ok {
Ok(Backend::Docker)
} else {
Err(NetError::NoBackend)
}
}
}
}
pub fn select_connector(pref: Preference) -> Result<Box<dyn Connector>, NetError> {
let native = crate::vpn::native::NativeConnector;
let docker = crate::vpn::docker::DockerConnector;
let backend = choose(pref, native.available(), docker.available())?;
Ok(match backend {
Backend::Native => Box::new(native),
Backend::Docker => Box::new(docker),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auto_prefers_native_then_docker() {
assert_eq!(
choose(Preference::Auto, true, true).unwrap(),
Backend::Native
);
assert_eq!(
choose(Preference::Auto, false, true).unwrap(),
Backend::Docker
);
assert!(matches!(
choose(Preference::Auto, false, false),
Err(NetError::NoBackend)
));
}
#[test]
fn forced_backends_require_availability() {
assert_eq!(
choose(Preference::Native, true, false).unwrap(),
Backend::Native
);
assert!(matches!(
choose(Preference::Native, false, true),
Err(NetError::NoBackend)
));
assert_eq!(
choose(Preference::Docker, false, true).unwrap(),
Backend::Docker
);
assert!(matches!(
choose(Preference::Docker, true, false),
Err(NetError::NoBackend)
));
}
}