1 use std::f64;
2
3 #[derive(Debug)]
4 pub enum ProtobufFloatParseError {
5 EmptyString,
6 CannotParseFloat,
7 }
8
9 pub type ProtobufFloatParseResult<T> = Result<T, ProtobufFloatParseError>;
10
11 pub const PROTOBUF_NAN: &str = "nan";
12 pub const PROTOBUF_INF: &str = "inf";
13
14 /// Format float as in protobuf `.proto` files
format_protobuf_float(f: f64) -> String15 pub fn format_protobuf_float(f: f64) -> String {
16 if f.is_nan() {
17 PROTOBUF_NAN.to_owned()
18 } else if f.is_infinite() {
19 if f > 0.0 {
20 format!("{}", PROTOBUF_INF)
21 } else {
22 format!("-{}", PROTOBUF_INF)
23 }
24 } else {
25 let i = f as i64;
26 if i as f64 == f {
27 // Older rust versions did print float without `.0` suffix
28 format!("{:?}.0", i)
29 } else {
30 // TODO: make sure doesn't lose precision
31 format!("{:?}", f)
32 }
33 }
34 }
35
36 /// Parse float from `.proto` format
parse_protobuf_float(s: &str) -> ProtobufFloatParseResult<f64>37 pub fn parse_protobuf_float(s: &str) -> ProtobufFloatParseResult<f64> {
38 if s.is_empty() {
39 return Err(ProtobufFloatParseError::EmptyString);
40 }
41 if s == PROTOBUF_NAN {
42 return Ok(f64::NAN);
43 }
44 if s == PROTOBUF_INF || s == format!("+{}", PROTOBUF_INF) {
45 return Ok(f64::INFINITY);
46 }
47 if s == format!("-{}", PROTOBUF_INF) {
48 return Ok(f64::NEG_INFINITY);
49 }
50 match s.parse() {
51 Ok(f) => Ok(f),
52 Err(_) => Err(ProtobufFloatParseError::CannotParseFloat),
53 }
54 }
55
56 #[cfg(test)]
57 mod test {
58 use super::*;
59
60 #[test]
test_format_protobuf_float()61 fn test_format_protobuf_float() {
62 assert_eq!("10.0", format_protobuf_float(10.0));
63 assert_eq!("-10.0", format_protobuf_float(-10.0));
64 assert_eq!("10.5", format_protobuf_float(10.5));
65 assert_eq!("-10.5", format_protobuf_float(-10.5));
66 }
67 }
68