diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a3de8bf --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "protocol" +version = "0.1.0" diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index b93cf3f..5eba4e2 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -1,14 +1,3 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +mod message; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +use message::{create_request_message, parse_response_message}; diff --git a/protocol/src/message.rs b/protocol/src/message.rs new file mode 100644 index 0000000..f9a463b --- /dev/null +++ b/protocol/src/message.rs @@ -0,0 +1,149 @@ +use std::error::Error; +use std::fmt; + +#[derive(PartialEq, Debug)] +pub enum MessageError { + EmptyResponse, + HeaderMissingValueCount, + HeaderValueCountIncorrectFormat(String), + MalformedValueReceived(String), + ValueNotFound(String, Vec), +} + +impl fmt::Display for MessageError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let message = match *self { + Self::EmptyResponse => "Got an empty response".to_string(), + Self::HeaderMissingValueCount => "Value count not found in the header".to_string(), + Self::HeaderValueCountIncorrectFormat(ref val) => { + format!("String could not be parsed as value count: {}", val) + } + Self::MalformedValueReceived(ref val) => { + format!("Could not parse value as float: {}", val) + } + Self::ValueNotFound(ref name, ref values) => format!( + "Value not found in the received values: {} in [{}]", + name, + values.join(", ") + ), + }; + + f.write_str(&message) + } +} + +impl Error for MessageError {} + +pub fn create_request_message(value: String) -> String { + format!("GETVAL {}", value) +} + +pub fn parse_response_message(resp: String, name: String) -> Result { + let mut lines = resp.split_terminator("\n"); + let header = lines.next().ok_or(MessageError::EmptyResponse)?; + let num_values_raw = header + .split(" ") + .next() + .ok_or(MessageError::HeaderMissingValueCount)?; + let num_values = num_values_raw + .parse::() + .map_err(|_| MessageError::HeaderValueCountIncorrectFormat(num_values_raw.to_string()))?; + + if num_values < 0 { + Err(MessageError::ValueNotFound(name, vec![])) + } else { + let mut values: Vec = vec![]; + + for line in lines { + let mut line_segment = line.split("="); + if let Some(val_name) = line_segment.next() { + if val_name == name + && let Some(val_raw) = line_segment.next() + { + return val_raw.parse::().map_err(|_| { + MessageError::MalformedValueReceived(num_values_raw.to_string()) + }); + } + values.push(val_name.to_string()); + } + } + + Err(MessageError::ValueNotFound(name, values)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! assert_almost_equal { + ($a:expr, $b:expr, $eps:expr) => { + assert!(($a - $b).abs() < $eps); + }; + } + + #[test] + fn test_create_request() { + assert_eq!( + create_request_message("some/value".to_string()), + "GETVAL some/value", + ); + } + + #[test] + fn test_parse_response_message_simple() { + let resp = vec!["1 Value found", "value=4.5"].join("\n"); + let result = parse_response_message(resp, "value".to_string()); + assert_almost_equal!(result.unwrap(), 4.5, 0.1); + } + + #[test] + fn test_parse_response_message_exponential() { + let resp = vec!["1 Value found", "value=4.514824e+02"].join("\n"); + let result = parse_response_message(resp, "value".to_string()); + assert_almost_equal!(result.unwrap(), 451.4824, 0.1); + } + + #[test] + fn test_parse_response_message_multiple_values() { + let resp = vec!["3 Values found", "v1=4.514824e+09", "v2=1.234", "v3=0.1"].join("\n"); + let result = parse_response_message(resp, "v2".to_string()); + assert_almost_equal!(result.unwrap(), 1.234, 0.1); + } + + #[test] + fn test_parse_response_message_multiple_values_not_found() { + let resp = vec!["3 Values found", "v1=4.514824e+09", "v2=1.234", "v3=0.1"].join("\n"); + let result = parse_response_message(resp, "v10".to_string()); + assert!(result.is_err()); + assert_eq!( + result, + Err(MessageError::ValueNotFound( + "v10".to_string(), + vec!["v1".to_string(), "v2".to_string(), "v3".to_string()] + )) + ); + } + + #[test] + fn test_parse_response_message_no_values() { + let resp = "0 Values found".to_string(); + let result = parse_response_message(resp, "value".to_string()); + assert!(result.is_err()); + assert_eq!( + result, + Err(MessageError::ValueNotFound("value".to_string(), vec![])) + ); + } + + #[test] + fn test_parse_response_message_field_not_found() { + let resp = "-1 Type `loadx' is unknown.".to_string(); + let result = parse_response_message(resp, "value".to_string()); + assert!(result.is_err()); + assert_eq!( + result, + Err(MessageError::ValueNotFound("value".to_string(), vec![])) + ); + } +}