use std::f64; #[derive(Debug)] pub enum ProtobufFloatParseError { EmptyString, CannotParseFloat, } pub type ProtobufFloatParseResult = Result; pub const PROTOBUF_NAN: &str = "nan"; pub const PROTOBUF_INF: &str = "inf"; /// Format float as in protobuf `.proto` files pub fn format_protobuf_float(f: f64) -> String { if f.is_nan() { PROTOBUF_NAN.to_owned() } else if f.is_infinite() { if f > 0.0 { format!("{}", PROTOBUF_INF) } else { format!("-{}", PROTOBUF_INF) } } else { let i = f as i64; if i as f64 == f { // Older rust versions did print float without `.0` suffix format!("{:?}.0", i) } else { // TODO: make sure doesn't lose precision format!("{:?}", f) } } } /// Parse float from `.proto` format pub fn parse_protobuf_float(s: &str) -> ProtobufFloatParseResult { if s.is_empty() { return Err(ProtobufFloatParseError::EmptyString); } if s == PROTOBUF_NAN { return Ok(f64::NAN); } if s == PROTOBUF_INF || s == format!("+{}", PROTOBUF_INF) { return Ok(f64::INFINITY); } if s == format!("-{}", PROTOBUF_INF) { return Ok(f64::NEG_INFINITY); } match s.parse() { Ok(f) => Ok(f), Err(_) => Err(ProtobufFloatParseError::CannotParseFloat), } } #[cfg(test)] mod test { use super::*; #[test] fn test_format_protobuf_float() { assert_eq!("10.0", format_protobuf_float(10.0)); assert_eq!("-10.0", format_protobuf_float(-10.0)); assert_eq!("10.5", format_protobuf_float(10.5)); assert_eq!("-10.5", format_protobuf_float(-10.5)); } }