128 lines
3.6 KiB
Rust
128 lines
3.6 KiB
Rust
use std::{cell::RefCell, collections::HashMap, fs::read_to_string, io::Result, rc::Rc};
|
|
|
|
#[derive(Debug)]
|
|
enum Entry {
|
|
Dir(Option<RME>, HashMap<String, RME>),
|
|
File(usize),
|
|
}
|
|
|
|
type RME = Rc<RefCell<Entry>>;
|
|
|
|
impl Entry {
|
|
pub fn new_dir<S: AsRef<str>>(parent: &Option<RME>, 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<S: AsRef<str>>(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<S: AsRef<str>>(&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<F: FnMut(&Self)>(&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::<Vec<_>>();
|
|
let size = split[0].parse::<usize>().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(())
|
|
}
|