118 lines
3.7 KiB
Rust
118 lines
3.7 KiB
Rust
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<File>,
|
|
}
|
|
|
|
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::<usize>())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.sum()
|
|
}
|
|
|
|
fn parse_command(
|
|
lines: &mut Peekable<Lines>,
|
|
cwd: &str,
|
|
directorys: &mut Vec<Directory>,
|
|
) -> Result<String, String> {
|
|
let command = lines.next().unwrap().split_whitespace().collect::<Vec<_>>();
|
|
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::<Vec<_>>();
|
|
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::<Vec<_>>().first() == Some(&"$") {
|
|
break;
|
|
}
|
|
let line = lines.next().unwrap();
|
|
let node = line.split_whitespace().collect::<Vec<_>>();
|
|
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}");
|
|
}
|