1
0
Fork 0
advent-of-code/src/bin/p07.rs

190 lines
4.5 KiB
Rust

#![feature(iterator_try_collect, absolute_path)]
use std::collections::HashMap;
use std::fs;
use std::io::{self, BufRead, Read};
use std::path::PathBuf;
mod parsing {
use nom::{
branch::alt,
bytes::complete::{is_not, tag},
character::complete::{digit1, multispace0},
combinator::{into, map, map_res},
sequence::{delimited, pair, preceded, separated_pair},
IResult,
};
#[derive(Debug, PartialEq)]
pub enum Command {
Ls,
Cd(String),
}
fn cd(input: &str) -> IResult<&str, Command> {
let (input, dir) =
preceded(delimited(multispace0, tag("cd"), multispace0), filename)(input)?;
Ok((input, Command::Cd(dir)))
}
fn ls(input: &str) -> IResult<&str, Command> {
let (input, _) = tag("ls")(input)?;
Ok((input, Command::Ls))
}
fn command(input: &str) -> IResult<&str, Command> {
preceded(delimited(multispace0, tag("$"), multispace0), alt((cd, ls)))(input)
}
#[derive(Debug, PartialEq)]
pub enum Listing {
Dir(String),
File(String, usize),
}
impl Listing {
pub fn path(&self) -> &String {
match self {
Self::Dir(s) => s,
Self::File(s, _) => s,
}
}
}
fn dir(input: &str) -> IResult<&str, Listing> {
let (input, f_name) = preceded(pair(tag("dir"), multispace0), filename)(input)?;
Ok((input, Listing::Dir(f_name)))
}
fn filename(input: &str) -> IResult<&str, String> {
map(is_not(" "), |r: &str| r.to_string())(input)
}
fn file(input: &str) -> IResult<&str, Listing> {
let (input, (f_size, f_name)) = separated_pair(
map_res(digit1, |s: &str| s.parse::<usize>()),
multispace0,
filename,
)(input)?;
Ok((input, Listing::File(f_name, f_size)))
}
fn listing(input: &str) -> IResult<&str, Listing> {
alt((dir, file))(input)
}
#[derive(Debug, PartialEq)]
pub enum Line {
Result(Listing),
Command(Command),
}
impl From<Listing> for Line {
fn from(value: Listing) -> Self {
Self::Result(value)
}
}
impl From<Command> for Line {
fn from(value: Command) -> Self {
Self::Command(value)
}
}
pub fn line(input: &str) -> IResult<&str, Line> {
alt((into(listing), into(command)))(input)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command() {
assert_eq!(Ok(("", Command::Ls)), command("$ ls"));
assert_eq!(
Ok(("", Command::Cd("poo".to_string()))),
command("$ cd poo")
);
}
#[test]
fn test_listing() {
assert_eq!(
Ok(("", Listing::Dir("cabbage".to_string()))),
listing("dir cabbage")
);
assert_eq!(
Ok(("", Listing::File("boob.txt".to_string(), 424242))),
listing("424242 boob.txt")
);
}
}
}
fn main() {
let filename = "etc/p07.txt";
let file = fs::File::open(filename).expect("Can't open file");
let lines: Vec<String> = io::BufReader::new(file)
.by_ref()
.lines()
.flatten()
.collect();
println!("PART ONE ---------------");
part1(&lines);
println!("\n PART TWO ---------------");
part2(&lines);
}
use parsing::{line, Command, Line, Listing};
use path_clean::clean;
#[derive(Debug)]
struct State {
cur_path: PathBuf,
filesystem: HashMap<String, usize>,
}
impl State {
fn new() -> Self {
State {
cur_path: PathBuf::from("/"),
filesystem: HashMap::new(),
}
}
fn process(&mut self, line: Line) {
match line {
Line::Command(cmd) => self.run(cmd),
Line::Result(listing) => self.process_result(listing),
}
}
fn run(&mut self, cmd: Command) {
if let Command::Cd(p) = cmd {
self.cur_path.push(p)
}
}
fn process_result(&mut self, res: Listing) {
let filename = clean(&self.cur_path.join(res.path()).to_string_lossy());
if let Listing::File(_, size) = res {
self.filesystem.insert(filename, size);
}
}
}
fn part1(lines: &[String]) {
let mut state = State::new();
lines
.iter()
.flat_map(|l| line(l))
.for_each(|(_, l)| state.process(l));
dbg!(state.filesystem);
}
fn part2(lines: &[String]) {}