Browse Source

Day 4 WIP parsing lines

Tyler Hallada 5 years ago
parent
commit
ac311a1b1c
6 changed files with 296 additions and 0 deletions
  1. 66 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 17 0
      inputs/4_test.txt
  4. 5 0
      inputs/4_test_malformed.txt
  5. 206 0
      src/day4.rs
  6. 1 0
      src/main.rs

+ 66 - 0
Cargo.lock

@@ -2,6 +2,7 @@
2 2
 name = "advent-of-code-2018"
3 3
 version = "0.1.0"
4 4
 dependencies = [
5
+ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
5 6
  "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
6 7
 ]
7 8
 
@@ -18,6 +19,16 @@ name = "cfg-if"
18 19
 version = "0.1.6"
19 20
 source = "registry+https://github.com/rust-lang/crates.io-index"
20 21
 
22
+[[package]]
23
+name = "chrono"
24
+version = "0.4.6"
25
+source = "registry+https://github.com/rust-lang/crates.io-index"
26
+dependencies = [
27
+ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
28
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
29
+ "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
30
+]
31
+
21 32
 [[package]]
22 33
 name = "lazy_static"
23 34
 version = "1.2.0"
@@ -38,6 +49,24 @@ dependencies = [
38 49
  "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
39 50
 ]
40 51
 
52
+[[package]]
53
+name = "num-integer"
54
+version = "0.1.39"
55
+source = "registry+https://github.com/rust-lang/crates.io-index"
56
+dependencies = [
57
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
58
+]
59
+
60
+[[package]]
61
+name = "num-traits"
62
+version = "0.2.6"
63
+source = "registry+https://github.com/rust-lang/crates.io-index"
64
+
65
+[[package]]
66
+name = "redox_syscall"
67
+version = "0.1.44"
68
+source = "registry+https://github.com/rust-lang/crates.io-index"
69
+
41 70
 [[package]]
42 71
 name = "regex"
43 72
 version = "1.1.0"
@@ -66,6 +95,16 @@ dependencies = [
66 95
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
67 96
 ]
68 97
 
98
+[[package]]
99
+name = "time"
100
+version = "0.1.41"
101
+source = "registry+https://github.com/rust-lang/crates.io-index"
102
+dependencies = [
103
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
104
+ "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)",
105
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
106
+]
107
+
69 108
 [[package]]
70 109
 name = "ucd-util"
71 110
 version = "0.1.3"
@@ -81,15 +120,42 @@ name = "version_check"
81 120
 version = "0.1.5"
82 121
 source = "registry+https://github.com/rust-lang/crates.io-index"
83 122
 
123
+[[package]]
124
+name = "winapi"
125
+version = "0.3.6"
126
+source = "registry+https://github.com/rust-lang/crates.io-index"
127
+dependencies = [
128
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
129
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
130
+]
131
+
132
+[[package]]
133
+name = "winapi-i686-pc-windows-gnu"
134
+version = "0.4.0"
135
+source = "registry+https://github.com/rust-lang/crates.io-index"
136
+
137
+[[package]]
138
+name = "winapi-x86_64-pc-windows-gnu"
139
+version = "0.4.0"
140
+source = "registry+https://github.com/rust-lang/crates.io-index"
141
+
84 142
 [metadata]
85 143
 "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
86 144
 "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
145
+"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
87 146
 "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
88 147
 "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
89 148
 "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
149
+"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
150
+"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
151
+"checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70"
90 152
 "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
91 153
 "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
92 154
 "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
155
+"checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c"
93 156
 "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
94 157
 "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
95 158
 "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
159
+"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
160
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
161
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

+ 1 - 0
Cargo.toml

@@ -5,4 +5,5 @@ authors = ["Tyler Hallada <tyler@hallada.net>"]
5 5
 edition = "2018"
6 6
 
7 7
 [dependencies]
8
+chrono = "0.4"
8 9
 regex = "1"

+ 17 - 0
inputs/4_test.txt

@@ -0,0 +1,17 @@
1
+[1518-11-01 00:00] Guard #10 begins shift
2
+[1518-11-01 00:05] falls asleep
3
+[1518-11-01 00:25] wakes up
4
+[1518-11-01 00:30] falls asleep
5
+[1518-11-01 00:55] wakes up
6
+[1518-11-01 23:58] Guard #99 begins shift
7
+[1518-11-02 00:40] falls asleep
8
+[1518-11-02 00:50] wakes up
9
+[1518-11-03 00:05] Guard #10 begins shift
10
+[1518-11-03 00:24] falls asleep
11
+[1518-11-03 00:29] wakes up
12
+[1518-11-04 00:02] Guard #99 begins shift
13
+[1518-11-04 00:36] falls asleep
14
+[1518-11-04 00:46] wakes up
15
+[1518-11-05 00:03] Guard #99 begins shift
16
+[1518-11-05 00:45] falls asleep
17
+[1518-11-05 00:55] wakes up

+ 5 - 0
inputs/4_test_malformed.txt

@@ -0,0 +1,5 @@
1
+[1518-11-01 00:00] Guard #10 begins shift
2
+[1518-11-01 00:05] falls asleep
3
+[1518-11-01 00:25] wakes up
4
+[1518-11-01 00:30] falls asleep
5
+[1518-11-01 00:55] dies

+ 206 - 0
src/day4.rs

@@ -0,0 +1,206 @@
1
+extern crate chrono;
2
+extern crate regex;
3
+
4
+use std::error::Error;
5
+use std::fs::File;
6
+use std::io::{BufRead, BufReader};
7
+use std::fmt;
8
+use std::collections::{HashMap, HashSet};
9
+use std::iter::FromIterator;
10
+
11
+use chrono::prelude::*;
12
+use regex::{Regex, Captures};
13
+
14
+const INPUT: &str = "inputs/4.txt";
15
+
16
+#[derive(Debug, PartialEq)]
17
+enum Record {
18
+    Start {
19
+        time: NaiveDateTime,
20
+        guard_id: u32,
21
+    },
22
+    Sleep {
23
+        time: NaiveDateTime,
24
+    },
25
+    Wake {
26
+        time: NaiveDateTime,
27
+    },
28
+}
29
+
30
+#[derive(Debug, Clone, PartialEq)]
31
+struct MalformedRecord {
32
+    details: String
33
+}
34
+
35
+impl MalformedRecord {
36
+    fn new(msg: &str) -> MalformedRecord {
37
+        MalformedRecord{ details: msg.to_string() }
38
+    }
39
+}
40
+
41
+impl fmt::Display for MalformedRecord {
42
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43
+        write!(f, "{}", self.details)
44
+    }
45
+}
46
+
47
+impl Error for MalformedRecord {
48
+    fn description(&self) -> &str {
49
+        &self.details
50
+    }
51
+}
52
+
53
+fn read_records(filename: &str) -> Result<Vec<Record>, Box<Error>> {
54
+    let mut records: Vec<Record> = Vec::new();
55
+    let record_regex =
56
+        Regex::new(r"\[(?P<timestamp>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2})\]\s(?:(?P<start>Guard #(?P<guard_id>\d+) begins shift)|(?P<sleep>falls asleep)|(?P<wake>wakes up))")?;
57
+    let file = File::open(filename)?;
58
+    for line in BufReader::new(file).lines() {
59
+        match record_regex.captures(&line?) {
60
+            Some(captures) => {
61
+                println!("{:?}", NaiveDateTime::parse_from_str(
62
+                    &get_captured_field(&captures, "timestamp")?,
63
+                    "%Y-%m-%d %H:%M")?);
64
+                if has_captured_field(&captures, "start")? {
65
+                    records.push(Record::Start {
66
+                        time: NaiveDateTime::parse_from_str(
67
+                            &get_captured_field(&captures, "timestamp")?,
68
+                            "%Y-%m-%d %H:%M")?,
69
+                        guard_id: get_captured_field(&captures, "guard_id")?.parse()?,
70
+                    });
71
+                } else if has_captured_field(&captures, "sleep")? {
72
+                    records.push(Record::Sleep {
73
+                        time: NaiveDateTime::parse_from_str(
74
+                            &get_captured_field(&captures, "timestamp")?,
75
+                            "%Y-%m-%d %H:%M")?,
76
+                    });
77
+                } else {
78
+                    records.push(Record::Wake {
79
+                        time: NaiveDateTime::parse_from_str(
80
+                            &get_captured_field(&captures, "timestamp")?,
81
+                            "%Y-%m-%d %H:%M")?,
82
+                    });
83
+                }
84
+            },
85
+            None => return Err(Box::new(MalformedRecord {
86
+                details: "Malformed record line, no fields could be found".to_string()
87
+            })),
88
+        };
89
+    }
90
+    Ok(records)
91
+}
92
+
93
+fn get_captured_field(captures: &Captures, field: &str) -> Result<String, Box<Error>> {
94
+    match captures.name(field) {
95
+        Some(capture) => Ok(String::from(capture.as_str())),
96
+        None => return Err(Box::new(MalformedRecord {
97
+            details: format!("Malformed record line, field {} could not be found", field)
98
+        }))
99
+    }
100
+}
101
+
102
+fn has_captured_field(captures: &Captures, field: &str) -> Result<bool, Box<Error>> {
103
+    match captures.name(field) {
104
+        Some(_) => Ok(true),
105
+        None => Ok(false)
106
+    }
107
+}
108
+
109
+
110
+#[cfg(test)]
111
+mod tests {
112
+    use super::*;
113
+
114
+    const TEST_INPUT: &str = "inputs/4_test.txt";
115
+    const TEST_INPUT_MALFORMED: &str = "inputs/4_test_malformed.txt";
116
+
117
+    #[test]
118
+    fn reads_records_file() {
119
+        assert_eq!(read_records(TEST_INPUT).unwrap(), vec![
120
+            Record::Start {
121
+                time: NaiveDateTime::parse_from_str(
122
+                          "1518-11-01 00:00", "%Y-%m-%d %H:%M").unwrap(),
123
+                guard_id: 10,
124
+            },
125
+            Record::Sleep {
126
+                time: NaiveDateTime::parse_from_str(
127
+                          "1518-11-01 00:05", "%Y-%m-%d %H:%M").unwrap(),
128
+            },
129
+            Record::Wake {
130
+                time: NaiveDateTime::parse_from_str(
131
+                          "1518-11-01 00:25", "%Y-%m-%d %H:%M").unwrap(),
132
+            },
133
+            Record::Sleep {
134
+                time: NaiveDateTime::parse_from_str(
135
+                          "1518-11-01 00:30", "%Y-%m-%d %H:%M").unwrap(),
136
+            },
137
+            Record::Wake {
138
+                time: NaiveDateTime::parse_from_str(
139
+                          "1518-11-01 00:55", "%Y-%m-%d %H:%M").unwrap(),
140
+            },
141
+            Record::Start {
142
+                time: NaiveDateTime::parse_from_str(
143
+                          "1518-11-01 23:58", "%Y-%m-%d %H:%M").unwrap(),
144
+                guard_id: 99,
145
+            },
146
+            Record::Sleep {
147
+                time: NaiveDateTime::parse_from_str(
148
+                          "1518-11-02 00:40", "%Y-%m-%d %H:%M").unwrap(),
149
+            },
150
+            Record::Wake {
151
+                time: NaiveDateTime::parse_from_str(
152
+                          "1518-11-02 00:50", "%Y-%m-%d %H:%M").unwrap(),
153
+            },
154
+            Record::Start {
155
+                time: NaiveDateTime::parse_from_str(
156
+                          "1518-11-03 00:05", "%Y-%m-%d %H:%M").unwrap(),
157
+                guard_id: 10,
158
+            },
159
+            Record::Sleep {
160
+                time: NaiveDateTime::parse_from_str(
161
+                          "1518-11-03 00:24", "%Y-%m-%d %H:%M").unwrap(),
162
+            },
163
+            Record::Wake {
164
+                time: NaiveDateTime::parse_from_str(
165
+                          "1518-11-03 00:29", "%Y-%m-%d %H:%M").unwrap(),
166
+            },
167
+            Record::Start {
168
+                time: NaiveDateTime::parse_from_str(
169
+                          "1518-11-04 00:02", "%Y-%m-%d %H:%M").unwrap(),
170
+                guard_id: 99,
171
+            },
172
+            Record::Sleep {
173
+                time: NaiveDateTime::parse_from_str(
174
+                          "1518-11-04 00:36", "%Y-%m-%d %H:%M").unwrap(),
175
+            },
176
+            Record::Wake {
177
+                time: NaiveDateTime::parse_from_str(
178
+                          "1518-11-04 00:46", "%Y-%m-%d %H:%M").unwrap(),
179
+            },
180
+            Record::Start {
181
+                time: NaiveDateTime::parse_from_str(
182
+                          "1518-11-05 00:03", "%Y-%m-%d %H:%M").unwrap(),
183
+                guard_id: 99,
184
+            },
185
+            Record::Sleep {
186
+                time: NaiveDateTime::parse_from_str(
187
+                          "1518-11-05 00:45", "%Y-%m-%d %H:%M").unwrap(),
188
+            },
189
+            Record::Wake {
190
+                time: NaiveDateTime::parse_from_str(
191
+                          "1518-11-05 00:55", "%Y-%m-%d %H:%M").unwrap(),
192
+            },
193
+        ]);
194
+    }
195
+
196
+    #[test]
197
+    fn errors_on_malformed_records_file() {
198
+        match read_records(TEST_INPUT_MALFORMED) {
199
+            Ok(_) => assert!(false, "read_records should have returned an error"),
200
+            Err(err) => assert_eq!(
201
+                (*err).description(),
202
+                "Malformed record line, no fields could be found".to_string(),
203
+            ),
204
+        }
205
+    }
206
+}

+ 1 - 0
src/main.rs

@@ -1,6 +1,7 @@
1 1
 mod day1;
2 2
 mod day2;
3 3
 mod day3;
4
+mod day4;
4 5
 
5 6
 fn main() {
6 7
     println!("Day 1:");