Skip to content

Commit a103111

Browse files
committed
Added support for summing floats
1 parent 3bbf90f commit a103111

File tree

3 files changed

+126
-47
lines changed

3 files changed

+126
-47
lines changed

src/lib.rs

+40-39
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,44 @@
1+
use std::fmt;
2+
use std::ops::Add;
3+
14
/// This enum represents the sum of a sequence of numbers that may be integers or floating point.
25
/// Integer is the default. When a floating point number is added to the sum, the type is converted
36
/// to Float.
4-
#[derive(Debug, PartialEq)]
7+
#[derive(Debug, PartialEq, Clone, Copy)]
58
pub enum Sum {
69
Integer(i128),
710
Float(f64),
811
}
912

10-
impl Sum {
11-
fn new() -> Self {
12-
Self::Integer(0)
13-
}
13+
impl Add for Sum {
14+
type Output = Self;
1415

15-
/// Adds `n` to the sum. The type (Integer or Float) of the Sum is unchanged.
16-
fn add_integer(&mut self, n: i128) {
17-
*self = match *self {
18-
Sum::Integer(i) => Sum::Integer(i + n),
19-
Sum::Float(f) => Sum::Float(f + n as f64),
20-
};
16+
/// Adds two Sums. If either is a Float, the result will be a Float.
17+
fn add(self, other: Self) -> Self {
18+
match (self, other) {
19+
(Sum::Integer(a), Sum::Integer(b)) => Sum::Integer(a + b),
20+
(Sum::Float(a), Sum::Float(b)) => Sum::Float(a + b),
21+
(Sum::Integer(a), Sum::Float(b)) => Sum::Float(a as f64 + b),
22+
(Sum::Float(a), Sum::Integer(b)) => Sum::Float(a + b as f64),
23+
}
2124
}
25+
}
2226

23-
/// Adds `n` to the sum. If the type is Intger, it's changed to Float after this operation.
24-
fn add_float(&mut self, n: f64) {
25-
*self = match *self {
26-
Sum::Integer(i) => Sum::Float(i as f64 + n),
27-
Sum::Float(f) => Sum::Float(f + n),
28-
};
27+
impl fmt::Display for Sum {
28+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29+
match self {
30+
Sum::Integer(n) => write!(f, "{n}"),
31+
Sum::Float(n) => write!(f, "{n}"),
32+
}
2933
}
3034
}
3135

32-
impl Default for Sum {
33-
fn default() -> Self {
34-
Self::new()
36+
impl fmt::UpperHex for Sum {
37+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38+
match self {
39+
Sum::Integer(n) => fmt::UpperHex::fmt(n, f),
40+
Sum::Float(n) => fmt::Display::fmt(n, f),
41+
}
3542
}
3643
}
3744

@@ -40,30 +47,24 @@ mod tests {
4047
use super::*;
4148

4249
#[test]
43-
fn default_works() {
44-
assert_eq!(Sum::new(), Default::default());
45-
}
46-
47-
#[test]
48-
fn integer_sum_works() {
49-
let mut sum = Sum::Integer(0);
50-
assert_eq!(sum, Sum::Integer(0));
51-
sum.add_integer(2);
52-
assert_eq!(sum, Sum::Integer(2));
50+
fn sum_integer_works() {
51+
let a = Sum::Integer(1);
52+
let b = Sum::Integer(2);
53+
assert_eq!(a + b, Sum::Integer(3));
5354
}
5455

5556
#[test]
56-
fn float_sum_works() {
57-
let mut sum = Sum::Float(1.2);
58-
assert_eq!(sum, Sum::Float(1.2));
59-
sum.add_float(0.7);
60-
assert_eq!(sum, Sum::Float(1.9));
57+
fn sum_float_works() {
58+
let a = Sum::Float(0.2);
59+
let b = Sum::Float(0.8);
60+
assert_eq!(a + b, Sum::Float(1.0));
6161
}
6262

6363
#[test]
64-
fn mixed_sum_works() {
65-
let mut sum = Sum::Integer(1);
66-
sum.add_float(0.2);
67-
assert_eq!(sum, Sum::Float(1.2));
64+
fn sum_mixed_works() {
65+
let a = Sum::Integer(1);
66+
let b = Sum::Float(0.2);
67+
assert_eq!(a + b, Sum::Float(1.2));
68+
assert_eq!(b + a, Sum::Float(1.2));
6869
}
6970
}

src/main.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use env_logger::Env;
33
use regex::Regex;
44
use std::fs;
55
use std::io::{self, BufRead, BufReader};
6+
use sumcol::Sum;
67

78
#[derive(Parser, Debug)]
89
#[command(version, about)]
@@ -41,7 +42,7 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
4142
v
4243
};
4344

44-
let mut sum = 0i128;
45+
let mut sum = Sum::Integer(0);
4546
for reader in readers {
4647
for (i, line) in reader.lines().enumerate() {
4748
let line = line?.trim().to_string();
@@ -59,17 +60,26 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
5960
None => (col, default_radix),
6061
};
6162
let n = match i128::from_str_radix(col, radix) {
62-
Ok(n) => n,
63+
Ok(n) => Sum::Integer(n),
6364
Err(e) => {
64-
log::info!("{e:?}, col={col:?}. Using 0 instead.");
65-
// If it parses as hex, warn the user that they may want to use -x.
66-
if i128::from_str_radix(col, 16).is_ok() {
67-
log::warn!("Failed to parse {col:?}. Consider using -x");
65+
log::info!("Not integer. {e:?}, col={col:?}, radix={radix:?}.");
66+
// Try parsing as a float
67+
match col.parse::<f64>() {
68+
Ok(n) => Sum::Float(n),
69+
Err(e) => {
70+
log::info!("Not float. {e:?}, col={col:?}.");
71+
// If it parses as hex, warn the user that they may want to use -x.
72+
if i128::from_str_radix(col, 16).is_ok() {
73+
log::warn!(
74+
"Failed to parse {col:?}, but it may be hex. Consider using -x"
75+
);
76+
}
77+
Sum::Integer(0)
78+
}
6879
}
69-
0
7080
}
7181
};
72-
sum += n;
82+
sum = sum + n;
7383
log::debug!("{i}: col={col:?}, n={n:?}, sum={sum:?}");
7484
}
7585
}

tests/cli.rs

+68
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,71 @@ fn sum_mixed_column_looks_like_number() -> TestResult {
142142
.stderr(predicate::str::contains("Consider using"));
143143
Ok(())
144144
}
145+
146+
#[test]
147+
fn sum_float() -> TestResult {
148+
let mut cmd = Command::cargo_bin("sumcol")?;
149+
let input = r"
150+
hello 2 blah
151+
hello 1.0 foo
152+
hello 2.2 oo
153+
blah 3e0 mumble
154+
";
155+
cmd.write_stdin(input)
156+
.args(["-f=2"])
157+
.assert()
158+
.success()
159+
.stdout(predicate::str::contains("8.2"));
160+
161+
// With -x, 3e0 will be parsed as 0x3e0 (decimal 992)
162+
let mut cmd = Command::cargo_bin("sumcol")?;
163+
cmd.write_stdin(input)
164+
.args(["-f=2", "-x"])
165+
.assert()
166+
.success()
167+
.stdout(predicate::str::contains("997.2"));
168+
Ok(())
169+
}
170+
171+
#[test]
172+
fn sum_float_0xhex() -> TestResult {
173+
let mut cmd = Command::cargo_bin("sumcol")?;
174+
let input = r"
175+
hello 2 blah
176+
hello 0xA blah
177+
hello 1.0 foo
178+
hello 2.2 oo
179+
";
180+
cmd.write_stdin(input)
181+
.args(["-f=2"])
182+
.assert()
183+
.success()
184+
.stdout(predicate::str::contains("15.2"));
185+
Ok(())
186+
}
187+
188+
#[test]
189+
fn sum_float_hex_flag() -> TestResult {
190+
let mut cmd = Command::cargo_bin("sumcol")?;
191+
let input = r"
192+
hello 2 blah
193+
hello A blah
194+
hello 1.0 foo
195+
hello 2.2 oo
196+
";
197+
// Without the -x flag, the A in the second line will be ignored.
198+
cmd.write_stdin(input)
199+
.args(["-f=2"])
200+
.assert()
201+
.success()
202+
.stdout(predicate::str::contains("5.2"));
203+
204+
// With -x, A will be treated as 0xA
205+
let mut cmd = Command::cargo_bin("sumcol")?;
206+
cmd.write_stdin(input)
207+
.args(["-f=2", "-x"])
208+
.assert()
209+
.success()
210+
.stdout(predicate::str::contains("15.2"));
211+
Ok(())
212+
}

0 commit comments

Comments
 (0)