• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

VolumeGraphics / havocompare / 8092875626

29 Feb 2024 08:09AM UTC coverage: 7.015% (-76.8%) from 83.823%
8092875626

push

github

rohdealx
use grcov

8046 of 114694 relevant lines covered (7.02%)

309.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/csv/value.rs
1
use schemars_derive::JsonSchema;
2
use serde::{Deserialize, Serialize};
3
use std::borrow::Cow;
4
use std::fmt::{Display, Formatter};
5

6
#[derive(Debug, Clone, JsonSchema, Deserialize, Serialize, PartialEq)]
×
7
pub struct Quantity {
8
    pub(crate) value: f64,
9
    pub(crate) unit: Option<String>,
10
}
11

12
fn next_up(val: f64) -> f64 {
×
13
    const TINY_BITS: u64 = 0x1; // Smallest positive f64.
×
14
    const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
×
15

×
16
    let bits = val.to_bits();
×
17
    if val.is_nan() || bits == f64::INFINITY.to_bits() {
×
18
        return val;
×
19
    }
×
20

×
21
    let abs = bits & CLEAR_SIGN_MASK;
×
22
    let next_bits = if abs == 0 {
×
23
        TINY_BITS
×
24
    } else if bits == abs {
×
25
        bits + 1
×
26
    } else {
27
        bits - 1
×
28
    };
29
    f64::from_bits(next_bits)
×
30
}
×
31

32
fn next_down(val: f64) -> f64 {
×
33
    const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64.
×
34
    const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
×
35

×
36
    let bits = val.to_bits();
×
37
    if val.is_nan() || bits == f64::NEG_INFINITY.to_bits() {
×
38
        return val;
×
39
    }
×
40

×
41
    let abs: u64 = bits & CLEAR_SIGN_MASK;
×
42
    let next_bits = if abs == 0 {
×
43
        NEG_TINY_BITS
×
44
    } else if bits == abs {
×
45
        bits - 1
×
46
    } else {
47
        bits + 1
×
48
    };
49
    f64::from_bits(next_bits)
×
50
}
×
51

52
impl Quantity {
53
    #[cfg(test)]
54
    pub(crate) fn new(value: f64, unit: Option<&str>) -> Self {
55
        Self {
56
            unit: unit.map(|s| s.to_owned()),
57
            value,
58
        }
59
    }
60

61
    /// This avoids the issue of `(a - b) > d` for `b = a + d` with small `d`
62
    pub(crate) fn minimal_diff(&self, rhs: &Quantity) -> f64 {
×
63
        let min = self.value.min(rhs.value);
×
64
        let max = self.value.max(rhs.value);
×
65
        let min_up = next_up(min);
×
66
        let max_down = next_down(max);
×
67
        next_down(max_down - min_up)
×
68
    }
×
69
}
70

71
impl Display for Quantity {
72
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
73
        if let Some(unit) = self.unit.as_deref() {
×
74
            write!(f, "{} {}", self.value, unit)
×
75
        } else {
76
            write!(f, "{}", self.value)
×
77
        }
78
    }
×
79
}
80

81
#[derive(Debug, PartialEq, Clone, Serialize)]
×
82
pub enum Value {
83
    Quantity(Quantity),
84
    String(String),
85
}
86

87
impl Display for Value {
88
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
89
        match &self {
×
90
            Value::Quantity(val) => {
×
91
                write!(f, "{val}").unwrap();
×
92
            }
×
93
            Value::String(val) => {
×
94
                write!(f, "'{val}'").unwrap();
×
95
            }
×
96
        }
97
        Ok(())
×
98
    }
×
99
}
100

101
impl Value {
102
    pub fn deleted() -> Value {
×
103
        Value::from_str("DELETED", &None)
×
104
    }
×
105

106
    fn get_numerical_value(field_split: &[&str]) -> Option<f64> {
×
107
        if field_split.len() == 1 || field_split.len() == 2 {
×
108
            return field_split.first().and_then(|s| s.parse::<f64>().ok());
×
109
        }
×
110
        None
×
111
    }
×
112

113
    pub fn from_str(s: &str, decimal_separator: &Option<char>) -> Value {
×
114
        let field_string: String = if let Some(delim) = decimal_separator {
×
115
            s.replace(*delim, ".")
×
116
        } else {
117
            s.into()
×
118
        };
119

120
        let field_split: Vec<_> = field_string.trim().split(' ').collect();
×
121

122
        if let Some(float_value) = Self::get_numerical_value(field_split.as_slice()) {
×
123
            Value::Quantity(Quantity {
×
124
                value: float_value,
×
125
                unit: field_split.get(1).map(|&s| s.to_owned()),
×
126
            })
×
127
        } else {
128
            Value::String(s.trim().to_owned())
×
129
        }
130
    }
×
131

132
    pub fn get_quantity(&self) -> Option<&Quantity> {
×
133
        match self {
×
134
            Value::Quantity(quantity) => Some(quantity),
×
135
            _ => None,
×
136
        }
137
    }
×
138

139
    pub fn get_string(&self) -> Option<String> {
×
140
        match self {
×
141
            Value::String(string) => Some(string.to_owned()),
×
142
            _ => None,
×
143
        }
144
    }
×
145

146
    pub fn as_str(&self) -> Cow<str> {
×
147
        match self {
×
148
            Value::String(str) => str.as_str().into(),
×
149
            Value::Quantity(quant) => quant.to_string().into(),
×
150
        }
151
    }
×
152
}
153

154
#[cfg(test)]
155
mod tests {
156
    use super::*;
157
    use crate::csv::Mode;
158
    #[test]
159
    fn trimming() {
160
        let val_spaced = Value::from_str(" value ", &None);
161
        let reference = Value::from_str("value", &None);
162
        assert_eq!(val_spaced, reference);
163
    }
164

165
    #[test]
166
    fn test_secure_diff() {
167
        for base in -30..=30 {
168
            for modulation in -30..=base {
169
                let magic_factor = 1.3;
170
                let num_one = magic_factor * 10.0f64.powi(base);
171
                let delta = magic_factor * 10.0f64.powi(modulation);
172
                let compare_mode = Mode::Absolute(delta.abs());
173
                let num_modulated = num_one + delta;
174
                let q1 = Quantity::new(num_one, None);
175
                let q2 = Quantity::new(num_modulated, None);
176
                // assert!(u_diff <= delta);
177
                assert!(compare_mode.in_tolerance(&q1, &q2));
178
            }
179
        }
180
    }
181
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc