use std::{cell::RefCell, collections::HashMap, fs::read_to_string, io::Result, rc::Rc}; #[derive(Debug)] enum Entry { Dir(Option, HashMap), File(usize), } type RME = Rc>; impl Entry { pub fn new_dir>(parent: &Option, name: S) -> RME { let this = Rc::new(RefCell::new(Entry::Dir(parent.clone(), HashMap::new()))); let this_clone = this.clone(); match parent { Some(parent) => match &mut *parent.borrow_mut() { Entry::Dir(_, ref mut children) => { children.insert(name.as_ref().to_owned(), this_clone); } _ => panic!("Attaching a child to a file"), }, None => (), } this } pub fn new_file>(parent: RME, name: S, size: usize) -> RME { let this = Rc::new(RefCell::new(Entry::File(size))); let this_clone = this.clone(); match &mut *parent.borrow_mut() { Entry::Dir(_, ref mut children) => { children.insert(name.as_ref().to_owned(), this_clone); } _ => panic!("Attaching a child to a file"), } this } pub fn size(&self) -> usize { match self { Entry::File(size) => *size, Entry::Dir(_, ref children) => children.values().map(|f| f.borrow().size()).sum(), } } pub fn parent(&self) -> RME { match self { Entry::Dir(parent, _) => parent.as_ref().unwrap().clone(), _ => panic!("Get parent for file"), } } pub fn file>(&self, name: S) -> RME { match self { Entry::Dir(_, ref children) => children.get(name.as_ref()).unwrap().clone(), _ => panic!("Get file for file"), } } pub fn iter_dirs(&self, f: &mut F) { match self { Entry::Dir(_, ref children) => { f(self); for child in children.values() { child.borrow().iter_dirs(f); } } _ => (), } } } fn main() -> Result<()> { let none = None; let root = Entry::new_dir(&none, "/"); let mut current = root.clone(); for line in read_to_string("input.txt")? .split("\n") .filter(|x| !x.is_empty()) { if line.starts_with("$ cd ") { let path = line.split_at(5).1; if path == "/" { current = root.clone(); } else if path == ".." { let next = current.borrow().parent(); current = next; } else { let next = current.borrow().file(path); current = next; } } else if line == "$ ls" { // ignore } else if line.starts_with("dir ") { let path = line.split_at(4).1; Entry::new_dir(&Some(current.clone()), path); } else { let split = line.split(" ").collect::>(); let size = split[0].parse::().unwrap(); let path = split[1]; Entry::new_file(current.clone(), path, size); } } let mut sum_p1 = 0; let mut smallest_p2 = usize::MAX; let to_free = 30000000usize - (70000000usize - root.borrow().size()); root.borrow().iter_dirs(&mut |dir| { let size = dir.size(); if size <= 100000 { sum_p1 += size } if size >= to_free && size <= smallest_p2 { smallest_p2 = size } }); println!("Part 1: {}", sum_p1); println!("Part 2: {}", smallest_p2); Ok(()) }