+use std::str::Lines;
+
+use itertools::Itertools;
+
+pub fn parse(s: &str) -> Lines {
+ s.lines()
+}
+
+fn calibration_value(line: &str) -> u32 {
+ let first = line.chars().find(|c| c.is_digit(10)).unwrap();
+ let last = line.chars().rev().find(|c| c.is_digit(10)).unwrap();
+ format!("{}{}", first, last).parse().unwrap()
+}
+
+pub fn calibration_sum(lines: &Lines) -> u32 {
+ lines.clone().map(calibration_value).sum()
+}
+
+fn calibration_value_corrected(line: &str) -> u32 {
+ let line_chars = line.chars().collect_vec();
+ let mut first: i32 = -1;
+ let mut last: i32 = -1;
+ let spelled_digits = [
+ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
+ ];
+ let mut i = 0;
+ while i < line.len() {
+ if let Some(d) = line_chars[i].to_digit(10) {
+ if first == -1 {
+ first = d as i32;
+ }
+ last = d as i32;
+ } else {
+ for j in 0..spelled_digits.len() {
+ let d = spelled_digits[j];
+ if line[i..].starts_with(d) {
+ if first == -1 {
+ first = j as i32;
+ }
+ last = j as i32;
+ i += 1;
+ // We can't skip an entire word because of cases like
+ // "twone", "nineight", etc.
+ break;
+ }
+ }
+ }
+
+ i += 1;
+ }
+ format!("{}{}", first, last).parse().unwrap()
+}
+
+pub fn calibration_sum_corrected(lines: &Lines) -> u32 {
+ lines.clone().map(calibration_value_corrected).sum()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ static CALIBRATION_LINES: &str = "1abc2
+ pqr3stu8vwx
+ a1b2c3d4e5f
+ treb7uchet";
+
+ #[test]
+ fn part1() {
+ let lines = parse(CALIBRATION_LINES);
+ assert_eq!(calibration_sum(&lines), 142);
+ }
+
+ static CALIBRATION_LINES_2: &str = "two1nine
+ eightwothree
+ abcone2threexyz
+ xtwone3four
+ 4nineeightseven2
+ zoneight234
+ 7pqrstsixteen";
+
+ #[test]
+ fn part2() {
+ let lines = parse(CALIBRATION_LINES_2);
+ assert_eq!(calibration_sum_corrected(&lines), 281);
+ }
+}