1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
pub use anyhow;
#[macro_export]
macro_rules! localpath {
($tt:tt) => {{
use ::std::path::Path;
let envvar = env!("CARGO_MANIFEST_DIR");
let basedir = Path::new(&envvar);
basedir.join($tt)
}};
}
#[macro_export]
macro_rules! parse_input {
($path:expr) => {
parse_input!($path, String)
};
($path:expr, $ty:ty) => {{
use ::std::boxed::Box;
use ::std::fs::File;
use ::std::io::Read;
use ::std::io::{BufRead, BufReader};
use ::std::path::PathBuf;
use $crate::files::anyhow;
let path = PathBuf::from($path);
let file = File::open(&path);
let input: Box<dyn Read> = if let Ok(f) = file {
Box::new(f)
} else {
Box::new(path.to_str().unwrap().as_bytes())
};
BufReader::new(input)
.lines()
.map(|bufline| {
anyhow::Context::context(
bufline,
"error iterating over bufreader",
)
.and_then(|line| {
anyhow::Context::context(
line.parse::<$ty>().map_err(|e| anyhow::anyhow!(e)),
"Unable to parse as type",
)
})
})
.collect::<anyhow::Result<Vec<_>>>()
}};
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
static U32_TEST: &str = "123\n456";
#[test]
fn test_localpath() {
let path = localpath!("foo.txt");
assert!(path.ends_with("aoc21-rust/aoc/foo.txt"));
}
#[test]
fn test_read_to_u32_from_file() {
let mut tmpfile = tempfile::NamedTempFile::new().unwrap();
write!(tmpfile, "{}", U32_TEST).unwrap();
let expected = vec![123_u32, 456];
let result = parse_input!(tmpfile.path(), u32);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_read_to_u32_from_str() {
let result = parse_input!(U32_TEST, u32);
let expected = vec![123_u32, 456];
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_read_to_string() {
let mut tmpfile = tempfile::NamedTempFile::new().unwrap();
write!(tmpfile, "123\n456").unwrap();
let expected: Vec<String> =
["123", "456"].into_iter().map(Into::into).collect();
let result = parse_input!(tmpfile.path()).unwrap();
assert_eq!(expected, result);
}
#[test]
fn test_read_to_custom_type() {
#[derive(Debug, PartialEq)]
struct Point {
x: u32,
y: u32,
}
impl std::str::FromStr for Point {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut splitter = s.split_whitespace();
match (splitter.next(), splitter.next()) {
(Some(x), Some(y)) => Ok(Point {
x: x.parse()?,
y: y.parse()?,
}),
_ => anyhow::bail!("Unable to parse"),
}
}
}
let mut tmpfile = tempfile::NamedTempFile::new().unwrap();
write!(tmpfile, "1 2\n3 4").unwrap();
let expected = vec![Point { x: 1, y: 2 }, Point { x: 3, y: 4 }];
let result = parse_input!(tmpfile.path(), Point).unwrap();
assert_eq!(expected, result);
}
}