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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
use std::io::{Error, ErrorKind}; use std::process::Command; mod tokens; #[derive(Debug, PartialEq)] pub enum PowerSource { Battery(u8), Other(String), } fn parse_life(input: &str) -> Option<u8> { input .split(';') .nth(0) .and_then(|v| v.split_whitespace().nth(2)) .and_then(|v| v.trim_end_matches('%').parse::<u8>().ok()) } pub fn parse<T>(input: T) -> Option<PowerSource> where T: std::convert::AsRef<str>, { let mut lines = input.as_ref().lines(); let (first, second) = (lines.next(), lines.next()); second.and_then(parse_life).and_then(|amount| { first.and_then(|line| { line.splitn(3, '\'').nth(1).map(|v| match v { "Battery Power" => PowerSource::Battery(amount), other => PowerSource::Other(String::from(other)), }) }) }) } pub fn draw(source: PowerSource) -> Option<char> { if let PowerSource::Battery(amount) = source { let token = match amount { 0..=10 => tokens::TEN, 11..=20 => tokens::TWENTY, 21..=30 => tokens::THIRTY, 31..=40 => tokens::FOURTY, 41..=50 => tokens::FIFTY, 51..=60 => tokens::SIXTY, 61..=70 => tokens::SEVENTY, 71..=80 => tokens::EIGHTY, 81..=90 => tokens::NINETY, _ => tokens::HUNDRED, }; return Some(token); } None } pub fn measure() -> Result<PowerSource, Error> { let result = Command::new("pmset").arg("-g").arg("batt").output()?; let output = String::from_utf8(result.stdout).map_err(|e| Error::new(ErrorKind::Other, e))?; parse(output).ok_or(Error::new(ErrorKind::NotFound, "Unable to produce measurement")) } #[cfg(test)] mod tests { use super::{parse, PowerSource}; #[test] fn none_when_unable_to_parse() { let result = parse("whoa"); assert!(result.is_none()); } #[test] fn battery_with_amount_100() { let result = parse( r#"Now drawing from 'Battery Power' -InternalBattery-0 (id=4522083) 100%; charged; 0:00 remaining present: true "#, ); assert_eq!(result, Some(PowerSource::Battery(100))) } #[test] fn none_invalid_amount() { let result = parse( r#"Now drawing from 'Battery Power' -InternalBattery-0 (id=4522083) abc%; charged; 0:00 remaining present: true "#, ); assert!(result.is_none()); } #[test] fn battery_with_amount_10() { let result = parse( r#"Now drawing from 'Battery Power' -InternalBattery-0 (id=4522083) 10%; charged; 0:00 remaining present: true "#, ); assert_eq!(result, Some(PowerSource::Battery(10))) } #[test] fn battery_with_amount_1() { let result = parse( r#"Now drawing from 'Battery Power' -InternalBattery-0 (id=4522083) 1%; charged; 0:00 remaining present: true "#, ); assert_eq!(result, Some(PowerSource::Battery(1))) } #[test] fn other_with_name() { let result = parse( r#"Now drawing from 'AC Power' -InternalBattery-0 (id=4522083) 100%; charged; 0:00 remaining present: true "#, ); assert_eq!(result, Some(PowerSource::Other(String::from("AC Power")))) } }