// Copyright 2022 Christian Ulrich // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use std::io; use std::iter; #[derive(Debug)] pub enum Node { Directory { name: String, children: Vec }, File { name: String, size: usize }, } pub struct FileSystem { root: Node, } impl Node { pub fn name(&self) -> &str { match self { Node::File { name, size: _ } => name, Node::Directory { name, children: _ } => name, } } pub fn size(&self) -> usize { match self { Node::File { name: _, size } => *size, Node::Directory { name: _, children } => { children.iter().fold(0, |acc, c| acc + c.size()) } } } pub fn walk(&self) -> Box + '_> { match self { Node::File { name: _, size: _ } => Box::new(iter::empty()), Node::Directory { name: _, children } => Box::new( children.iter().chain( children .iter() .skip(1) .fold(children[0].walk(), |acc, c| Box::new(acc.chain(c.walk()))), ), ), } } } impl FileSystem { pub fn from_stdin() -> Result { let mut line = String::new(); if let Ok(_) = io::stdin().read_line(&mut line) { return Ok(FileSystem { root: FileSystem::parse_dir(&line)?, }); } Err("input file is empty") } pub fn root(&self) -> &Node { &self.root } fn parse_file(line: &str) -> Result { if let Some((size, name)) = line.trim().split_once(' ') { return Ok(Node::File { name: name.into(), size: size.parse().map_err(|_| "failed to parse file")?, }); } Err("failed to parse file") } fn parse_dir(line: &str) -> Result { if !line.starts_with("$ cd ") { return Err("cannot parse directory"); } let name = line[5..].trim(); let mut children = vec![]; let mut line = String::new(); match io::stdin().read_line(&mut line) { Ok(_) if { line == "$ ls\n" } => (), _ => return Err("cannot parse directory"), } loop { line.clear(); match io::stdin().read_line(&mut line) { Ok(_) if { line.starts_with("dir ") } => (), Ok(_) if { line.trim().is_empty() || line == "$ cd ..\n" } => break, Ok(_) if { line.starts_with("$ cd ") } => children.push(Self::parse_dir(&line)?), Ok(_) => children.push(Self::parse_file(&line)?), _ => assert!(false), } } Ok(Node::Directory { name: name.into(), children: children, }) } }