1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
use super::{from_str, FromFile}; use serde_yaml; use std::str::FromStr; error_chain! { errors { InvalidPortSpecsFile { description("invalid port specs file") } InvalidPortSpecs { description("invalid port specs") } InvalidPortState(invalid: String) { description("invalid port state") display("invalid port state: {}", invalid) } } } #[derive(Debug, Deserialize)] pub struct PortSpecs { #[serde(rename = "portspecs")] pub port_specs: Vec<PortSpec>, } impl FromStr for PortSpecs { type Err = Error; fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> { PortSpecs::from_bytes(s.as_bytes()) } } impl PortSpecs { fn from_bytes(buffer: &[u8]) -> Result<Self> { serde_yaml::from_slice(buffer).chain_err(|| ErrorKind::InvalidPortSpecs) } } impl FromFile for PortSpecs {} #[derive(Debug, Deserialize)] pub struct PortSpec { pub name: String, pub ports: Vec<Port>, } #[derive(Debug, Deserialize)] pub struct Port { pub id: u16, #[serde(deserialize_with = "from_str")] pub state: PortState, } #[derive(Debug)] pub enum PortState { Closed, Open, } impl FromStr for PortState { type Err = Error; fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> { let s = s.trim().to_lowercase(); match s.as_ref() { "closed" => Ok(PortState::Closed), "open" => Ok(PortState::Open), _ => Err(Error::from_kind(ErrorKind::InvalidPortState(s))), } } } #[cfg(test)] mod tests { use super::*; use spectral::prelude::*; #[test] fn parse_portspecs_okay() { let s = r##"--- portspecs: - name: Group A ports: - id: 22 state: closed - id: 25 state: open - name: Group B ports: - id: 80 state: open - id: 443 state: open "##; let res = PortSpecs::from_str(s); println!("{:#?}", res); assert_that(&res).is_ok(); let port_specs = res.unwrap(); assert_that(&port_specs.port_specs).has_length(2); } }