use std::{fs::read_to_string, iter::Peekable, str::Lines}; #[derive(PartialEq, Debug)] struct File { name: String, size: usize, } impl File { fn size(&self) -> usize { self.size } } #[derive(PartialEq, Debug)] struct Directory { path: String, files: Vec, } fn size_of_dir(dirs: &[Directory], dir: &str) -> usize { dirs.iter() .filter_map(|d| { if d.path.contains(dir) { Some(d.files.iter().map(File::size).sum::()) } else { None } }) .sum() } fn parse_command( lines: &mut Peekable, cwd: &str, directorys: &mut Vec, ) -> Result { let command = lines.next().unwrap().split_whitespace().collect::>(); if command.len() < 2 || command[0] != "$" { return Err("first line is not a command".to_owned()); } match command[1] { "cd" => { if command.len() != 3 { return Err("cd needs exactly 1 argument".to_owned()); } match command[2] { "/" => Ok("/".to_owned()), ".." => { if cwd == "/" { Ok(cwd.to_owned()) } else { let dirs = cwd.split_inclusive('/').collect::>(); let nwd = dirs[..(dirs.len() - 1)] .iter() .fold(String::new(), |s, v| s + v); Ok(nwd) } } dir => Ok(cwd.to_owned() + dir + "/"), } } "ls" => { while let Some(line) = lines.peek() { if line.split_whitespace().collect::>().first() == Some(&"$") { break; } let line = lines.next().unwrap(); let node = line.split_whitespace().collect::>(); if node.len() != 2 { return Err("ls needs two inputs per line".to_owned()); } match node[0] { "dir" => directorys.push(Directory { path: cwd.to_owned() + node[1] + "/", files: vec![], }), size => directorys .iter_mut() .find(|d| d.path == cwd) .unwrap() .files .push(File { name: node[1].to_owned(), size: size.parse().expect("invalid Size"), }), } } Ok(cwd.to_owned()) } _ => Err("unknown command".to_owned()), } } fn main() { const TOTAL_SPACE: usize = 70000000; const NEEDED_FREE: usize = 30000000; let input = read_to_string("input.txt").unwrap(); let mut lines = input.lines().peekable(); let mut dirs = vec![Directory { path: "/".to_owned(), files: vec![], }]; let mut cwd = "/".to_owned(); while lines.peek().is_some() { cwd = parse_command(&mut lines, &cwd, &mut dirs).expect("invalid input"); } let sizes = dirs.iter().map(|d| size_of_dir(&dirs, &d.path)); let sol1: usize = sizes.clone().filter(|s| s < &100_000).sum(); println!("{sol1}"); let used = size_of_dir(&dirs, "/"); let free = TOTAL_SPACE - used; let additional = NEEDED_FREE - free; println!("{used} Bytes used, {free} Bytes free, {additional} Bytes need to be freed"); let sol2 = sizes.filter(|s| s >= &additional).min().unwrap(); println!("Deleting Directory with size {sol2}"); }