blob: 78ca622c02a5a083c1069198a4cf56c0459ffedd [file] [log] [blame]
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -07001use std::f64;
2
3#[derive(Debug)]
4pub enum ProtobufFloatParseError {
5 EmptyString,
6 CannotParseFloat,
7}
8
9pub type ProtobufFloatParseResult<T> = Result<T, ProtobufFloatParseError>;
10
11pub const PROTOBUF_NAN: &str = "nan";
12pub const PROTOBUF_INF: &str = "inf";
13
14/// Format float as in protobuf `.proto` files
15pub 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
37pub 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)]
57mod test {
58 use super::*;
59
60 #[test]
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}