use regex::Regex;
use std::fmt;
use std::str::FromStr;
use super::helpers::*;
macro_rules! mode_enum {
(// This pattern matches zero or more doc comments and metadata
// attributes.
$(#[$flag:meta])*
pub enum $name:ident {
// This pattern matches a list of enum values with no args.
$(
$(#[$flag0:meta])*
($tag0:expr) => $item0:ident
),*
;
$(
$(#[$flag1:meta])*
($tag1:expr) => $item1:ident($arg:ty)
),*
}) => {
$(#[$flag])*
pub enum $name {
$(
$(#[$flag0])*
$item0,
)*
$(
$(#[$flag1])*
$item1($arg),
)*
}
impl_interpolatable_value!($name);
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
$( &$name::$item0 => write!(f, $tag0), )*
$( &$name::$item1(ref name) =>
write!(f, "{}:{}", $tag1, name), )*
}
}
}
impl FromStr for $name {
type Err = InvalidValueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
lazy_static! {
static ref COMPOUND: Regex =
Regex::new("^([-a-z]+):(.+)$").unwrap();
}
match s {
$( $tag0 => Ok($name::$item0), )*
_ => {
let caps = try!(COMPOUND.captures(s).ok_or_else(|| {
InvalidValueError::new(stringify!($name), s)
}));
let valstr = caps.at(2).unwrap();
match caps.at(1).unwrap() {
$( $tag1 => {
let value = try!(FromStr::from_str(valstr).map_err(|_| {
InvalidValueError::new(stringify!($name),
valstr)
}));
Ok($name::$item1(value))
})*
_ => Err(InvalidValueError::new(stringify!($name), s))
}
}
}
}
}
}
}
mode_enum! {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NetworkMode {
("bridge") => Bridge,
("host") => Host,
("none") => None
;
("service") => Service(String),
("container") => Container(String)
}
}
#[test]
fn network_mode_has_a_string_representation() {
let pairs = vec!(
(NetworkMode::Bridge, "bridge"),
(NetworkMode::Host, "host"),
(NetworkMode::None, "none"),
(NetworkMode::Service("foo".to_owned()), "service:foo"),
(NetworkMode::Container("foo".to_owned()), "container:foo"),
);
for (mode, s) in pairs {
assert_eq!(mode.to_string(), s);
assert_eq!(mode, NetworkMode::from_str(s).unwrap());
}
}
mode_enum! {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PidMode {
("host") => Host
;
("container") => Container(String)
}
}
mode_enum! {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IpcMode {
("host") => Host
;
("container") => Container(String)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RestartMode {
No,
OnFailure(Option<u32>),
Always,
UnlessStopped,
}
impl_interpolatable_value!(RestartMode);
impl fmt::Display for RestartMode {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
&RestartMode::No => write!(f, "no"),
&RestartMode::OnFailure(None) => write!(f, "on-failure"),
&RestartMode::OnFailure(Some(retries)) =>
write!(f, "on-failure:{}", retries),
&RestartMode::Always => write!(f, "always"),
&RestartMode::UnlessStopped => write!(f, "unless-stopped"),
}
}
}
impl FromStr for RestartMode {
type Err = InvalidValueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
lazy_static! {
static ref COMPOUND: Regex =
Regex::new("^([-a-z]+):(.+)$").unwrap();
}
match s {
"no" => Ok(RestartMode::No),
"on-failure" => Ok(RestartMode::OnFailure(None)),
"always" => Ok(RestartMode::Always),
"unless-stopped" => Ok(RestartMode::UnlessStopped),
_ => {
let caps = try!(COMPOUND.captures(s).ok_or_else(|| {
InvalidValueError::new("restart-mode", s)
}));
let valstr = caps.at(2).unwrap();
match caps.at(1).unwrap() {
"on-failure" => {
let value = try!(FromStr::from_str(valstr).map_err(|_| {
InvalidValueError::new("restart mode", valstr)
}));
Ok(RestartMode::OnFailure(Some(value)))
}
_ => Err(InvalidValueError::new("restart mode", s)),
}
}
}
}
}
#[test]
fn restart_mode_has_a_string_representation() {
let pairs = vec!(
(RestartMode::No, "no"),
(RestartMode::OnFailure(None), "on-failure"),
(RestartMode::OnFailure(Some(3)), "on-failure:3"),
(RestartMode::Always, "always"),
(RestartMode::UnlessStopped, "unless-stopped"),
);
for (mode, s) in pairs {
assert_eq!(mode.to_string(), s);
assert_eq!(mode, RestartMode::from_str(s).unwrap());
}
}