use std::collections::HashMap; use std::process::Command; use std::io; #[derive(Debug, PartialEq, Eq)] pub struct Unit { pub name: String, pub type_: String, pub active_state: String, pub sub_state: String, } pub fn parse_key_value(line: &str) -> Option<(String, String)> { let pos = line.find('='); pos.map(|pos| { let (k, v) = line.split_at(pos); (k.to_owned(), v[1..].to_owned()) }) } #[test] fn test_parse_key_value() { assert_eq!(None, parse_key_value("ab")); assert_eq!(Some(("a".to_owned(), "b".to_owned())), parse_key_value("a=b")); assert_eq!(Some(("a".to_owned(), "b=c".to_owned())), parse_key_value("a=b=c")); } fn make_unit(info: HashMap) -> Option { fn from_id(id: &str) -> Option { id.rsplit('.').next().map(|t| t.to_owned()) }; fn cloned(s: &String) -> String { s.clone() } let id = info.get("Id").map(cloned); let type_ = id.as_ref().and_then(|id| from_id(&id)); let active_state = info.get("ActiveState").map(cloned); let sub_state = info.get("SubState").map(cloned); if let (Some(id), Some(type_), Some(active_state), Some(sub_state)) = (id, type_, active_state, sub_state) { Some(Unit { name: id, type_: type_, active_state: active_state, sub_state: sub_state }) } else { None } } #[test] fn test_make_unit() { let s = |s: &str| -> String { s.to_owned() }; let info = [ (s("Id"), s("Test.service")), (s("ActiveState"), s("active")), (s("SubState"), s("running")), ] .iter().cloned().collect::>(); let actual = make_unit(info).unwrap();; assert_eq!(s("Test.service"), actual.name); let info = HashMap::new(); assert_eq!(None, make_unit(info)); } pub fn get_units(filter: &str) -> io::Result> { let mut units = Vec::new(); let status = try!(Command::new("systemctl") .args(&["show", filter]).output()); let mut unit_info = HashMap::new(); for line in String::from_utf8_lossy(&status.stdout).split('\n') { if let Some((k, v)) = parse_key_value(line) { unit_info.insert(k, v); } else { make_unit(unit_info).map(|u| units.push(u)); unit_info = HashMap::new(); } } return Ok(units); }