switched to workspaces.
This should let me make a cross-day library.
This commit is contained in:
		
							parent
							
								
									1469c3a32b
								
							
						
					
					
						commit
						242989bb95
					
				
					 57 changed files with 156 additions and 89 deletions
				
			
		
							
								
								
									
										11
									
								
								days/day07/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								days/day07/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "day07"
 | 
			
		||||
authors.workspace = true
 | 
			
		||||
description.workspace = true
 | 
			
		||||
version.workspace = true
 | 
			
		||||
edition.workspace = true
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
once_cell.workspace = true
 | 
			
		||||
regex.workspace = true
 | 
			
		||||
thiserror.workspace = true
 | 
			
		||||
							
								
								
									
										305
									
								
								days/day07/src/file_tree.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								days/day07/src/file_tree.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,305 @@
 | 
			
		|||
use std::{
 | 
			
		||||
    cell::RefCell,
 | 
			
		||||
    fmt::Display,
 | 
			
		||||
    ops::Deref,
 | 
			
		||||
    rc::{Rc, Weak},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
 | 
			
		||||
#[derive(Error, Debug)]
 | 
			
		||||
pub enum FileTreeError {
 | 
			
		||||
    #[error("Directory operation on file")]
 | 
			
		||||
    IsFile,
 | 
			
		||||
    #[error("File operation on directory")]
 | 
			
		||||
    IsDir,
 | 
			
		||||
    #[error("File not found")]
 | 
			
		||||
    FileNotFound,
 | 
			
		||||
    #[error("File already exists")]
 | 
			
		||||
    FileAlreadyExists,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type WeakNodeRef = Weak<RefCell<Node>>;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct NodeRef(pub Rc<RefCell<Node>>);
 | 
			
		||||
 | 
			
		||||
impl Deref for NodeRef {
 | 
			
		||||
    type Target = Rc<RefCell<Node>>;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        &self.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Rc<RefCell<Node>>> for NodeRef {
 | 
			
		||||
    fn from(value: Rc<RefCell<Node>>) -> Self {
 | 
			
		||||
        NodeRef(value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<NodeRef> for Rc<RefCell<Node>> {
 | 
			
		||||
    fn from(value: NodeRef) -> Self {
 | 
			
		||||
        value.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NodeRef {
 | 
			
		||||
    pub fn add_node(&mut self, mut node: Node) -> Result<NodeRef, FileTreeError> {
 | 
			
		||||
        node.set_parent(self);
 | 
			
		||||
        self.borrow_mut().add_children(node)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_dir(name: String) -> NodeRef {
 | 
			
		||||
        NodeRef(Rc::new(RefCell::new(Node::new_dir(name))))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn add_file(&mut self, name: String, size: usize) -> Result<NodeRef, FileTreeError> {
 | 
			
		||||
        self.add_node(Node::new_file(name, size))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn add_dir(&mut self, name: String) -> Result<NodeRef, FileTreeError> {
 | 
			
		||||
        self.add_node(Node::new_dir(name))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_all_dirs(&self) -> Vec<NodeRef> {
 | 
			
		||||
        let mut ret = Vec::new();
 | 
			
		||||
        match &self.borrow().contents {
 | 
			
		||||
            Contents::Size(_) => {}
 | 
			
		||||
            Contents::Children(c) => {
 | 
			
		||||
                ret.push(NodeRef(Rc::clone(self)));
 | 
			
		||||
                for node in c {
 | 
			
		||||
                    ret.append(&mut node.get_all_dirs())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        ret
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct Node {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    parent: Option<WeakNodeRef>,
 | 
			
		||||
    contents: Contents,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Node {
 | 
			
		||||
    pub fn new_dir(name: String) -> Node {
 | 
			
		||||
        Node {
 | 
			
		||||
            name,
 | 
			
		||||
            parent: None,
 | 
			
		||||
            contents: Contents::Children(Vec::new()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_file(name: String, size: usize) -> Node {
 | 
			
		||||
        Node {
 | 
			
		||||
            name,
 | 
			
		||||
            parent: None,
 | 
			
		||||
            contents: Contents::Size(size),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_total_size(&self) -> usize {
 | 
			
		||||
        match &self.contents {
 | 
			
		||||
            Contents::Size(s) => *s,
 | 
			
		||||
            Contents::Children(c) => c.iter().map(|f| f.borrow().get_total_size()).sum(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn set_parent(&mut self, newparent: &NodeRef) {
 | 
			
		||||
        self.parent = Some(Rc::downgrade(newparent));
 | 
			
		||||
    }
 | 
			
		||||
    // does not set the parent, needs to be done on the NodeRef. (this is why this func isnt pub).
 | 
			
		||||
    // takes onwership of the node to make sure its not owned by another tree.
 | 
			
		||||
    fn add_children(&mut self, node: Node) -> Result<NodeRef, FileTreeError> {
 | 
			
		||||
        match self.contents {
 | 
			
		||||
            Contents::Size(_) => Err(FileTreeError::IsFile),
 | 
			
		||||
            Contents::Children(ref mut c) => {
 | 
			
		||||
                for file in c.iter() {
 | 
			
		||||
                    if file.borrow().name == node.name {
 | 
			
		||||
                        return Err(FileTreeError::FileAlreadyExists);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                let rc = Rc::new(RefCell::new(node));
 | 
			
		||||
                c.push(NodeRef(Rc::clone(&rc)));
 | 
			
		||||
                Ok(NodeRef(Rc::clone(&rc)))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn remove_child_by_name(&mut self, name: &str) -> Result<(), FileTreeError> {
 | 
			
		||||
        match self.contents {
 | 
			
		||||
            Contents::Size(_) => Err(FileTreeError::IsFile),
 | 
			
		||||
            Contents::Children(ref mut c) => {
 | 
			
		||||
                for (i, file) in c.iter().enumerate() {
 | 
			
		||||
                    if file.borrow().name == name {
 | 
			
		||||
                        c.remove(i);
 | 
			
		||||
                        return Ok(());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Err(FileTreeError::FileNotFound)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_parent(&self) -> Option<NodeRef> {
 | 
			
		||||
        match &self.parent {
 | 
			
		||||
            Some(w) => Some(NodeRef(w.clone().upgrade()?)),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_size(&self) -> Result<usize, FileTreeError> {
 | 
			
		||||
        match self.contents {
 | 
			
		||||
            Contents::Size(s) => Ok(s),
 | 
			
		||||
            Contents::Children(_) => Err(FileTreeError::IsDir),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn set_size(&mut self, size: usize) -> Result<(), FileTreeError> {
 | 
			
		||||
        match self.contents {
 | 
			
		||||
            Contents::Size(ref mut s) => {
 | 
			
		||||
                *s = size;
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
            Contents::Children(_) => Err(FileTreeError::IsDir),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_children(&self) -> Result<impl Iterator<Item = NodeRef> + '_, FileTreeError> {
 | 
			
		||||
        match &self.contents {
 | 
			
		||||
            Contents::Size(_) => Err(FileTreeError::IsFile),
 | 
			
		||||
            Contents::Children(c) => Ok(c.iter().map(|n| NodeRef(Rc::clone(n)))),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_child_by_name(&self, name: &str) -> Result<NodeRef, FileTreeError> {
 | 
			
		||||
        match &self.contents {
 | 
			
		||||
            Contents::Size(_) => Err(FileTreeError::IsFile),
 | 
			
		||||
            Contents::Children(c) => {
 | 
			
		||||
                for file in c.iter() {
 | 
			
		||||
                    if file.borrow().name == name {
 | 
			
		||||
                        return Ok(NodeRef(Rc::clone(file)));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Err(FileTreeError::FileNotFound)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
enum Contents {
 | 
			
		||||
    Size(usize),
 | 
			
		||||
    Children(Vec<NodeRef>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for NodeRef {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        write!(f, "{}", self.borrow())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for Node {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        write!(f, "- {} {}", self.name, self.contents)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for Contents {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Contents::Size(s) => write!(f, "(file, size = {})", s),
 | 
			
		||||
            Contents::Children(c) => {
 | 
			
		||||
                writeln!(f, "(dir)").expect("I have no clue how this could fail");
 | 
			
		||||
                for node in c {
 | 
			
		||||
                    //padding
 | 
			
		||||
                    for line in format!("{}", node).lines() {
 | 
			
		||||
                        writeln!(f, " {line}")?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_dir_construction() {
 | 
			
		||||
        let mut root = NodeRef::new_dir("/".to_string());
 | 
			
		||||
        let mut cursor = root.add_node(Node::new_dir("a".to_string())).unwrap();
 | 
			
		||||
        cursor = cursor.add_dir("e".to_string()).unwrap();
 | 
			
		||||
        cursor.add_file("i".to_string(), 584).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("f".to_string(), 29116).unwrap();
 | 
			
		||||
        cursor.add_file("g".to_string(), 2557).unwrap();
 | 
			
		||||
        cursor.add_file("h.lst".to_string(), 62596).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("b.txt".to_string(), 14848514).unwrap();
 | 
			
		||||
        cursor.add_file("c.dat".to_string(), 8504156).unwrap();
 | 
			
		||||
        cursor = cursor.add_dir("d".to_string()).unwrap();
 | 
			
		||||
        cursor.add_file("j".to_string(), 4060174).unwrap();
 | 
			
		||||
        cursor.add_file("d.log".to_string(), 8033020).unwrap();
 | 
			
		||||
        cursor.add_file("d.ext".to_string(), 5626152).unwrap();
 | 
			
		||||
        cursor.add_file("k".to_string(), 7214296).unwrap();
 | 
			
		||||
        assert_eq!(Rc::clone(root.deref()).borrow().get_total_size(), 48381165);
 | 
			
		||||
        println!("{}", root);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_size_calcs() {
 | 
			
		||||
        let mut root = NodeRef::new_dir("/".to_string());
 | 
			
		||||
        let mut dirs: Vec<NodeRef> = Vec::new();
 | 
			
		||||
        let mut cursor = root.add_dir("a".to_string()).unwrap();
 | 
			
		||||
        dirs.push(NodeRef(Rc::clone(&cursor)));
 | 
			
		||||
        cursor = cursor.add_dir("e".to_string()).unwrap();
 | 
			
		||||
        dirs.push(NodeRef(Rc::clone(&cursor)));
 | 
			
		||||
        cursor.add_file("i".to_string(), 584).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("f".to_string(), 29116).unwrap();
 | 
			
		||||
        cursor.add_file("g".to_string(), 2557).unwrap();
 | 
			
		||||
        cursor.add_file("h.lst".to_string(), 62596).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("b.txt".to_string(), 14848514).unwrap();
 | 
			
		||||
        cursor.add_file("c.dat".to_string(), 8504156).unwrap();
 | 
			
		||||
        cursor = cursor.add_node(Node::new_dir("d".to_string())).unwrap();
 | 
			
		||||
        dirs.push(NodeRef(Rc::clone(&cursor)));
 | 
			
		||||
        cursor.add_file("j".to_string(), 4060174).unwrap();
 | 
			
		||||
        cursor.add_file("d.log".to_string(), 8033020).unwrap();
 | 
			
		||||
        cursor.add_file("d.ext".to_string(), 5626152).unwrap();
 | 
			
		||||
        cursor.add_file("k".to_string(), 7214296).unwrap();
 | 
			
		||||
        assert_eq!(Rc::clone(root.deref()).borrow().get_total_size(), 48381165);
 | 
			
		||||
        assert_eq!(Rc::clone(dirs[0].deref()).borrow().get_total_size(), 94853);
 | 
			
		||||
        assert_eq!(Rc::clone(dirs[1].deref()).borrow().get_total_size(), 584);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Rc::clone(dirs[2].deref()).borrow().get_total_size(),
 | 
			
		||||
            24933642
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_get_all_dirs() {
 | 
			
		||||
        let mut root = NodeRef::new_dir("/".to_string());
 | 
			
		||||
        let mut cursor = root.add_dir("a".to_string()).unwrap();
 | 
			
		||||
        cursor = cursor.add_dir("e".to_string()).unwrap();
 | 
			
		||||
        cursor.add_file("i".to_string(), 584).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("f".to_string(), 29116).unwrap();
 | 
			
		||||
        cursor.add_file("g".to_string(), 2557).unwrap();
 | 
			
		||||
        cursor.add_file("h.lst".to_string(), 62596).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("b.txt".to_string(), 14848514).unwrap();
 | 
			
		||||
        cursor.add_file("c.dat".to_string(), 8504156).unwrap();
 | 
			
		||||
        cursor = cursor.add_node(Node::new_dir("d".to_string())).unwrap();
 | 
			
		||||
        cursor.add_file("j".to_string(), 4060174).unwrap();
 | 
			
		||||
        cursor.add_file("d.log".to_string(), 8033020).unwrap();
 | 
			
		||||
        cursor.add_file("d.ext".to_string(), 5626152).unwrap();
 | 
			
		||||
        cursor.add_file("k".to_string(), 7214296).unwrap();
 | 
			
		||||
        let dirs = root.get_all_dirs();
 | 
			
		||||
        let sizes: Vec<usize> = dirs.iter().map(|d| d.borrow().get_total_size()).collect();
 | 
			
		||||
        println!("{:?}", sizes);
 | 
			
		||||
        assert_eq!(Rc::clone(root.deref()).borrow().get_total_size(), 48381165);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Rc::clone(dirs[0].deref()).borrow().get_total_size(),
 | 
			
		||||
            48381165
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(Rc::clone(dirs[1].deref()).borrow().get_total_size(), 94853);
 | 
			
		||||
        assert_eq!(Rc::clone(dirs[2].deref()).borrow().get_total_size(), 584);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Rc::clone(dirs[3].deref()).borrow().get_total_size(),
 | 
			
		||||
            24933642
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1030
									
								
								days/day07/src/input.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1030
									
								
								days/day07/src/input.txt
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										19
									
								
								days/day07/src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								days/day07/src/main.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
use crate::file_tree::NodeRef;
 | 
			
		||||
 | 
			
		||||
mod part1;
 | 
			
		||||
mod part2;
 | 
			
		||||
mod parser;
 | 
			
		||||
mod file_tree;
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let input = include_str!("./input.txt");
 | 
			
		||||
    let structured_input = parser::parse(input);
 | 
			
		||||
 | 
			
		||||
    println!("Part One");
 | 
			
		||||
    println!("Result: {}", part1::part1(NodeRef(Rc::clone(&structured_input))));
 | 
			
		||||
 | 
			
		||||
    println!("Part Two");
 | 
			
		||||
    println!("Result: {}", part2::part2(NodeRef(Rc::clone(&structured_input))));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										262
									
								
								days/day07/src/parser.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								days/day07/src/parser.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,262 @@
 | 
			
		|||
use std::ops::Deref;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
use crate::file_tree::*;
 | 
			
		||||
use once_cell::sync::Lazy;
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
 | 
			
		||||
static IS_COMMAND_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\$").unwrap());
 | 
			
		||||
static PARSE_CD_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\$ cd (\S*)$").unwrap());
 | 
			
		||||
static PARSE_LS_ENTRY_REGEX: Lazy<Regex> =
 | 
			
		||||
    Lazy::new(|| Regex::new(r"^([[:alnum:]]*) (\S*)$").unwrap());
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
pub enum Command {
 | 
			
		||||
    CdRoot,
 | 
			
		||||
    CdUp,
 | 
			
		||||
    Cd(String),
 | 
			
		||||
    Ls(Vec<LsEntry>),
 | 
			
		||||
}
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
pub enum LsEntry {
 | 
			
		||||
    Dir(String),
 | 
			
		||||
    File(ParseFile),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
pub struct ParseFile {
 | 
			
		||||
    size: usize,
 | 
			
		||||
    name: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//parses a single line
 | 
			
		||||
pub fn parse_to_commands(input: &str) -> Vec<Command> {
 | 
			
		||||
    let mut ret = Vec::new();
 | 
			
		||||
    let mut lines = input.lines().peekable();
 | 
			
		||||
    while lines.peek().is_some() {
 | 
			
		||||
        let line = lines.next().unwrap();
 | 
			
		||||
        if line == "$ ls" {
 | 
			
		||||
            ret.push(Command::Ls(parse_ls(&mut lines)));
 | 
			
		||||
        } else {
 | 
			
		||||
            let captures = PARSE_CD_REGEX
 | 
			
		||||
                .captures(line)
 | 
			
		||||
                .unwrap_or_else(|| panic!("invalid line {}", line));
 | 
			
		||||
 | 
			
		||||
            ret.push(match &captures[1] {
 | 
			
		||||
                ".." => Command::CdUp,
 | 
			
		||||
                "/" => Command::CdRoot,
 | 
			
		||||
                s => Command::Cd(s.to_string()),
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_ls(lines: &mut std::iter::Peekable<std::str::Lines<'_>>) -> Vec<LsEntry> {
 | 
			
		||||
    let mut ret: Vec<LsEntry> = Vec::new();
 | 
			
		||||
    while lines.peek().is_some() {
 | 
			
		||||
        // if the next line is a command, then we are at the end of the ls listing.
 | 
			
		||||
        let line = lines.peek().expect("no next line");
 | 
			
		||||
        if IS_COMMAND_REGEX.is_match(line) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        let captures = PARSE_LS_ENTRY_REGEX
 | 
			
		||||
            .captures(line)
 | 
			
		||||
            .unwrap_or_else(|| panic!("invalid line {}", line));
 | 
			
		||||
        ret.push(match &captures[1] {
 | 
			
		||||
            "dir" => LsEntry::Dir(captures[2].to_string()),
 | 
			
		||||
            _ => LsEntry::File(ParseFile {
 | 
			
		||||
                size: str::parse(&captures[1]).unwrap_or_else(|_| panic!("invalid line {}", line)),
 | 
			
		||||
                name: captures[2].to_string(),
 | 
			
		||||
            }),
 | 
			
		||||
        });
 | 
			
		||||
        lines.next();
 | 
			
		||||
    }
 | 
			
		||||
    ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn parse(input: &str) -> NodeRef {
 | 
			
		||||
    commands_to_tree(parse_to_commands(input))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn commands_to_tree(input: Vec<Command>) -> NodeRef {
 | 
			
		||||
    let root = NodeRef::new_dir("/".to_string());
 | 
			
		||||
    let mut cursor = NodeRef(Rc::clone(&root));
 | 
			
		||||
    for command in input {
 | 
			
		||||
        match command {
 | 
			
		||||
            Command::CdRoot => cursor = NodeRef(Rc::clone(&root)),
 | 
			
		||||
            Command::CdUp => cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap(),
 | 
			
		||||
            Command::Cd(name) => cursor = cursor.add_dir(name).unwrap(),
 | 
			
		||||
            Command::Ls(ls) => {
 | 
			
		||||
                for entry in ls {
 | 
			
		||||
                    match entry {
 | 
			
		||||
                        LsEntry::Dir(_) => {
 | 
			
		||||
                            //dirs dont exist until you cd into them.
 | 
			
		||||
                        }
 | 
			
		||||
                        LsEntry::File(f) => {
 | 
			
		||||
                            cursor.add_file(f.name, f.size).unwrap();
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    root
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_to_commands() {
 | 
			
		||||
        let input = concat!(
 | 
			
		||||
            "$ cd /\n",
 | 
			
		||||
            "$ ls\n",
 | 
			
		||||
            "dir a\n",
 | 
			
		||||
            "14848514 b.txt\n",
 | 
			
		||||
            "8504156 c.dat\n",
 | 
			
		||||
            "dir d\n",
 | 
			
		||||
            "$ cd a\n",
 | 
			
		||||
            "$ ls\n",
 | 
			
		||||
            "dir e\n",
 | 
			
		||||
            "29116 f\n",
 | 
			
		||||
            "2557 g\n",
 | 
			
		||||
            "62596 h.lst\n",
 | 
			
		||||
            "$ cd e\n",
 | 
			
		||||
            "$ ls\n",
 | 
			
		||||
            "584 i\n",
 | 
			
		||||
            "$ cd ..\n",
 | 
			
		||||
            "$ cd ..\n",
 | 
			
		||||
            "$ cd d\n",
 | 
			
		||||
            "$ ls\n",
 | 
			
		||||
            "4060174 j\n",
 | 
			
		||||
            "8033020 d.log\n",
 | 
			
		||||
            "5626152 d.ext\n",
 | 
			
		||||
            "7214296 k"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            parse_to_commands(input),
 | 
			
		||||
            vec![
 | 
			
		||||
                Command::CdRoot,
 | 
			
		||||
                Command::Ls(vec![
 | 
			
		||||
                    LsEntry::Dir(String::from("a")),
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 14848514,
 | 
			
		||||
                        name: String::from("b.txt")
 | 
			
		||||
                    }),
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 8504156,
 | 
			
		||||
                        name: String::from("c.dat")
 | 
			
		||||
                    }),
 | 
			
		||||
                    LsEntry::Dir(String::from("d"))
 | 
			
		||||
                ]),
 | 
			
		||||
                Command::Cd(String::from("a")),
 | 
			
		||||
                Command::Ls(vec![
 | 
			
		||||
                    LsEntry::Dir(String::from("e")),
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 29116,
 | 
			
		||||
                        name: String::from("f")
 | 
			
		||||
                    }),
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 2557,
 | 
			
		||||
                        name: String::from("g")
 | 
			
		||||
                    }),
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 62596,
 | 
			
		||||
                        name: String::from("h.lst")
 | 
			
		||||
                    }),
 | 
			
		||||
                ]),
 | 
			
		||||
                Command::Cd(String::from("e")),
 | 
			
		||||
                Command::Ls(vec![LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 584,
 | 
			
		||||
                    name: String::from("i")
 | 
			
		||||
                }),]),
 | 
			
		||||
                Command::CdUp,
 | 
			
		||||
                Command::CdUp,
 | 
			
		||||
                Command::Cd(String::from("d")),
 | 
			
		||||
                Command::Ls(vec![
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 4060174,
 | 
			
		||||
                        name: String::from("j")
 | 
			
		||||
                    }),
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 8033020,
 | 
			
		||||
                        name: String::from("d.log")
 | 
			
		||||
                    }),
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 5626152,
 | 
			
		||||
                        name: String::from("d.ext")
 | 
			
		||||
                    }),
 | 
			
		||||
                    LsEntry::File(ParseFile {
 | 
			
		||||
                        size: 7214296,
 | 
			
		||||
                        name: String::from("k")
 | 
			
		||||
                    }),
 | 
			
		||||
                ]),
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_commands_to_tree() {
 | 
			
		||||
        let input = vec![
 | 
			
		||||
            Command::CdRoot,
 | 
			
		||||
            Command::Ls(vec![
 | 
			
		||||
                LsEntry::Dir(String::from("a")),
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 14848514,
 | 
			
		||||
                    name: String::from("b.txt"),
 | 
			
		||||
                }),
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 8504156,
 | 
			
		||||
                    name: String::from("c.dat"),
 | 
			
		||||
                }),
 | 
			
		||||
                LsEntry::Dir(String::from("d")),
 | 
			
		||||
            ]),
 | 
			
		||||
            Command::Cd(String::from("a")),
 | 
			
		||||
            Command::Ls(vec![
 | 
			
		||||
                LsEntry::Dir(String::from("e")),
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 29116,
 | 
			
		||||
                    name: String::from("f"),
 | 
			
		||||
                }),
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 2557,
 | 
			
		||||
                    name: String::from("g"),
 | 
			
		||||
                }),
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 62596,
 | 
			
		||||
                    name: String::from("h.lst"),
 | 
			
		||||
                }),
 | 
			
		||||
            ]),
 | 
			
		||||
            Command::Cd(String::from("e")),
 | 
			
		||||
            Command::Ls(vec![LsEntry::File(ParseFile {
 | 
			
		||||
                size: 584,
 | 
			
		||||
                name: String::from("i"),
 | 
			
		||||
            })]),
 | 
			
		||||
            Command::CdUp,
 | 
			
		||||
            Command::CdUp,
 | 
			
		||||
            Command::Cd(String::from("d")),
 | 
			
		||||
            Command::Ls(vec![
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 4060174,
 | 
			
		||||
                    name: String::from("j"),
 | 
			
		||||
                }),
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 8033020,
 | 
			
		||||
                    name: String::from("d.log"),
 | 
			
		||||
                }),
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 5626152,
 | 
			
		||||
                    name: String::from("d.ext"),
 | 
			
		||||
                }),
 | 
			
		||||
                LsEntry::File(ParseFile {
 | 
			
		||||
                    size: 7214296,
 | 
			
		||||
                    name: String::from("k"),
 | 
			
		||||
                }),
 | 
			
		||||
            ]),
 | 
			
		||||
        ];
 | 
			
		||||
        let tree = commands_to_tree(input);
 | 
			
		||||
        println!("{}", tree);
 | 
			
		||||
        assert_eq!(Rc::clone(tree.deref()).borrow().get_total_size(), 48381165)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								days/day07/src/part1.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								days/day07/src/part1.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
use crate::file_tree::*;
 | 
			
		||||
 | 
			
		||||
pub fn part1(input: NodeRef) -> usize {
 | 
			
		||||
    let dirs = input.get_all_dirs();
 | 
			
		||||
    dirs.iter()
 | 
			
		||||
        .map(|d| d.borrow().get_total_size())
 | 
			
		||||
        .filter(|s| *s <= 100000)
 | 
			
		||||
        .sum()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use std::{ops::Deref, rc::Rc};
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_part1() {
 | 
			
		||||
        let mut root = NodeRef::new_dir("/".to_string());
 | 
			
		||||
        let mut cursor = root.add_node(Node::new_dir("a".to_string())).unwrap();
 | 
			
		||||
        cursor = cursor.add_dir("e".to_string()).unwrap();
 | 
			
		||||
        cursor.add_file("i".to_string(), 584).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("f".to_string(), 29116).unwrap();
 | 
			
		||||
        cursor.add_file("g".to_string(), 2557).unwrap();
 | 
			
		||||
        cursor.add_file("h.lst".to_string(), 62596).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("b.txt".to_string(), 14848514).unwrap();
 | 
			
		||||
        cursor.add_file("c.dat".to_string(), 8504156).unwrap();
 | 
			
		||||
        cursor = cursor.add_dir("d".to_string()).unwrap();
 | 
			
		||||
        cursor.add_file("j".to_string(), 4060174).unwrap();
 | 
			
		||||
        cursor.add_file("d.log".to_string(), 8033020).unwrap();
 | 
			
		||||
        cursor.add_file("d.ext".to_string(), 5626152).unwrap();
 | 
			
		||||
        cursor.add_file("k".to_string(), 7214296).unwrap();
 | 
			
		||||
        assert_eq!(part1(root), 95437);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								days/day07/src/part2.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								days/day07/src/part2.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
use crate::file_tree::*;
 | 
			
		||||
 | 
			
		||||
const TOTAL_SPACE: usize = 70000000;
 | 
			
		||||
const NEEDED_SPACE: usize = 30000000;
 | 
			
		||||
 | 
			
		||||
pub fn part2(input: NodeRef) -> usize {
 | 
			
		||||
    let used_space = input.borrow().get_total_size();
 | 
			
		||||
    let unused_space = TOTAL_SPACE - used_space;
 | 
			
		||||
    let space_to_free = NEEDED_SPACE - unused_space;
 | 
			
		||||
    let dirs = input.get_all_dirs();
 | 
			
		||||
    dirs.iter()
 | 
			
		||||
        .map(|d| d.borrow().get_total_size())
 | 
			
		||||
        .filter(|s| *s >= space_to_free)
 | 
			
		||||
        .min()
 | 
			
		||||
        .unwrap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use std::{ops::Deref, rc::Rc};
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_part2() {
 | 
			
		||||
        let mut root = NodeRef::new_dir("/".to_string());
 | 
			
		||||
        let mut cursor = root.add_node(Node::new_dir("a".to_string())).unwrap();
 | 
			
		||||
        cursor = cursor.add_dir("e".to_string()).unwrap();
 | 
			
		||||
        cursor.add_file("i".to_string(), 584).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("f".to_string(), 29116).unwrap();
 | 
			
		||||
        cursor.add_file("g".to_string(), 2557).unwrap();
 | 
			
		||||
        cursor.add_file("h.lst".to_string(), 62596).unwrap();
 | 
			
		||||
        cursor = Rc::clone(cursor.deref()).borrow().get_parent().unwrap();
 | 
			
		||||
        cursor.add_file("b.txt".to_string(), 14848514).unwrap();
 | 
			
		||||
        cursor.add_file("c.dat".to_string(), 8504156).unwrap();
 | 
			
		||||
        cursor = cursor.add_dir("d".to_string()).unwrap();
 | 
			
		||||
        cursor.add_file("j".to_string(), 4060174).unwrap();
 | 
			
		||||
        cursor.add_file("d.log".to_string(), 8033020).unwrap();
 | 
			
		||||
        cursor.add_file("d.ext".to_string(), 5626152).unwrap();
 | 
			
		||||
        cursor.add_file("k".to_string(), 7214296).unwrap();
 | 
			
		||||
        assert_eq!(part2(root), 24933642);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue