nrc_protobuf_codegen/
float.rs

1use 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}