190 lines
4.5 KiB
Rust
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]) {}
|