use std::cell::RefCell; use std::fs::File; use std::io::{prelude::*, BufReader}; use std::rc::Rc; trait Sizer { fn size(&self) -> usize; } impl Sizer for Vec { fn size(&self) -> usize { self.iter().sum() } } impl Sizer for Vec { fn size(&self) -> usize { self.iter().map(|x| x.size()).sum() } } #[derive(Clone, Debug)] struct MyFile { _name: String, size: usize, } impl Sizer for MyFile { fn size(&self) -> usize { self.size } } #[derive(Clone, Debug)] struct MyDir { name: String, objects: Vec, parent_dir: Option>>, } impl MyDir { fn move_up(&self) -> Option>> { self.parent_dir.clone() } fn move_down(&self, dir: impl Into) -> Option>> { let dir = dir.into(); Some( self.objects .iter() .filter_map(|x| match x { FileSystemTypes::MyDir(y) => Some(y), _ => None, }) .find(|x| *x.borrow().name == dir)? .clone(), ) } fn touch(&mut self, name: impl Into, size: usize) { self.objects.push(FileSystemTypes::MyFile(MyFile { _name: name.into(), size, })); } //has to be a static method... fn mkdir(self_: Rc>, name: impl Into) { self_ .borrow_mut() .objects .push(FileSystemTypes::MyDir(Rc::new(RefCell::new(MyDir::new( name, Some(self_.clone()), ))))); //me.clone() } fn new(name: impl Into, parent_dir: Option>>) -> Self { let name: String = name.into(); MyDir { name, objects: Vec::new(), parent_dir, } } } impl Sizer for MyDir { fn size(&self) -> usize { self.objects.size() } } #[derive(Clone, Debug)] enum FileSystemTypes { MyFile(MyFile), MyDir(Rc>), } impl Sizer for FileSystemTypes { fn size(&self) -> usize { match self { FileSystemTypes::MyFile(file) => file.size(), FileSystemTypes::MyDir(dir) => dir.borrow().size(), } } } fn recurse_part1(collector: &mut Vec, cwd: &MyDir) -> usize { let cwd_size: usize = cwd .objects .iter() .map(|x| match x { FileSystemTypes::MyFile(y) => y.size(), FileSystemTypes::MyDir(dir) => recurse_part1(collector, &dir.borrow()), }) .sum(); collector.push(cwd_size); cwd_size } fn main() -> std::io::Result<()> { //Read in file let file = File::open("input")?; let reader = BufReader::new(file); //create root file object on heap let root = Rc::new(RefCell::new(MyDir::new("/", None))); let mut ls_mode = false; //set a pointer to the currently on MyDir, in this case start at roo let mut cursor = root.clone(); // loop through the input files lines reader.lines().for_each(|line| { //need to unwrap the line cause lines() returns an Option let line = line.unwrap(); //if we are listing files we need to get the information from the input if ls_mode && line.as_bytes()[0] != b'$' { //do adding to cursor match line.split_whitespace().collect::>()[..] { ["dir", name] => MyDir::mkdir(cursor.clone(), name), [size, name] => cursor .borrow_mut() .touch(name, size.parse::().unwrap()), _ => panic!("oops {}", line), }; // end the for_each return; } ls_mode = false; //parse all other lines as commands cursor = match line.split_whitespace().collect::>()[..] { ["$", "ls"] => { ls_mode = true; cursor.clone() } ["$", "cd", "/"] => root.clone(), //set current directory back to root ["$", "cd", ".."] => cursor.borrow().move_up().unwrap(), ["$", "cd", dir] => cursor.borrow().move_down(dir).unwrap(), _ => panic!("unknown command {}", line), } }); let mut part1 = Vec::new(); let max = recurse_part1(&mut part1, &root.borrow()); let part1_ans: usize = part1.iter().filter(|x| **x <= 100_000).sum(); println!("Part 1: {}", part1_ans); //part 2 let free_space = 70_000_000_usize - max; let needed_clear_space = 30_000_000_usize - free_space; let part2 = part1.iter().filter(|x| **x >= needed_clear_space).min(); println!("Part 2: {}", part2.unwrap()); Ok(()) }