From 60f122de78a1f3af7953b1c86a0d99918ec51b53 Mon Sep 17 00:00:00 2001 From: Noah Swerhun Date: Wed, 13 Dec 2023 14:50:12 -0600 Subject: [PATCH] finally organized code --- src/build_rule.rs | 140 ++++++++++++++++ src/main.rs | 414 +--------------------------------------------- src/makefile.rs | 104 ++++++++++++ src/rule_list.rs | 174 +++++++++++++++++++ 4 files changed, 423 insertions(+), 409 deletions(-) create mode 100644 src/build_rule.rs create mode 100644 src/makefile.rs create mode 100644 src/rule_list.rs diff --git a/src/build_rule.rs b/src/build_rule.rs new file mode 100644 index 0000000..adf4bc1 --- /dev/null +++ b/src/build_rule.rs @@ -0,0 +1,140 @@ +use std::{path::PathBuf, fs}; +use crate::ARGS; + +use path_clean::PathClean; +use regex::Regex; + +use crate::makefile::*; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum BuildRuleKind { + Object(T), + Target(T), + Directory(T), + Convenience(T), +} + +#[derive(Debug,Clone)] +pub struct BuildRule { + pub target: String, + pub prerequisites: Vec, + pub recipe_commands: Vec, +} + +impl BuildRule { + pub fn get_headers_from_c_file(path: &str, makefile: &Makefile) -> Vec { + let mut headers: Vec = Vec::new(); + let file_contents = fs::read_to_string(path).unwrap_or_else(|e| { + panic!("error: could not read file {path}: {e}"); + }); + + let file_path = PathBuf::from(path); + let file_dir = file_path.parent().unwrap(); + + let includes_regex = Regex::new(r#"#include\s+"(.*?)""#).unwrap(); + let caps = includes_regex.captures_iter(&file_contents); + 'fileloop: for (_, [include_file]) in caps.map(|c| c.extract()) { + let path = file_dir.join(PathBuf::from(include_file)).clean(); + match path.exists() { + true => { + headers.push(String::from(path.to_str().unwrap())); + continue; + }, + false => (), + } + + for dir in makefile.include_dirs.iter() { + let dir = PathBuf::from(&dir); + let path = dir.join(PathBuf::from(include_file)).clean(); + match path.exists() { + true => { + headers.push(String::from(path.to_str().unwrap())); + continue 'fileloop; + }, + false => (), + } + } + + let path = file_dir.join(PathBuf::from(include_file)); + headers.push(String::from(path.to_str().unwrap())); + } + + headers + } + pub fn new_from_c_file(path: &str, makefile: &Makefile) -> BuildRule { + + let mut prerequisites: Vec = Vec::new(); + + prerequisites.push(String::from(path)); + + prerequisites.append(&mut BuildRule::get_headers_from_c_file(path, makefile)); + + prerequisites.push(String::from("| $(OBJDIR)")); + + let file_path = PathBuf::from(path); + + let obj_name = file_path.with_extension("o"); + let obj_name = obj_name.file_name().unwrap(); + + let obj_path = PathBuf::from("$(OBJDIR)").join(obj_name); + let obj_path = obj_path.to_str().unwrap(); + + let compile_command = format!("$(CC) $(CFLAGS) $(INCS) -o {} -c {}", obj_path, path); + + BuildRule { target: String::from(obj_path), prerequisites, recipe_commands: vec![compile_command] } + } +} + +impl ToString for BuildRuleKind { + fn to_string(&self) -> String { + let rule = match self { + BuildRuleKind::Object(v) => v, + BuildRuleKind::Target(v) => v, + BuildRuleKind::Directory(v) => v, + BuildRuleKind::Convenience(v) => v, + }; + let mut ret = String::new(); + ret.push_str(&format!("{}: ", rule.target)); + ret.push_str(&rule.prerequisites.join(" ")); + ret.push_str(&format!("\n")); + + ARGS.with(|args| { + let mut command_prefix = "\t"; + let mut special = false; + if args.pretty { + command_prefix = "\t@"; + let verb = match self { + BuildRuleKind::Object(_) => "Building", + BuildRuleKind::Target(_) => "Linking", + BuildRuleKind::Directory(_) => "Creating directory", + BuildRuleKind::Convenience(_) => { + special = true; + if rule.target.contains("clean") { + ret.push_str(&format!("{}printf '\\e[90m:: Cleaning up ...\\e[0m'\n", command_prefix)); + } + if rule.target.contains("run") { + ret.push_str(&format!("{}printf '\\e[34m:: Running\\e[0m \\e[1m%s\\e[0m \\e[34m...\\e[0m\\n' $(TARGET)\n", command_prefix)); + } + "" + }, + }; + if !special { + ret.push_str(&format!("{}printf '\\e[90m:: {}\\e[0m \\e[1m%s\\e[0m \\e[90m...\\e[0m' {}\n", command_prefix, verb, + (PathBuf::from(&rule.target)).file_name().unwrap().to_str().unwrap())); + } + } + + ret.push_str(&rule.recipe_commands.iter() + .map(|v| format!("{}{}", command_prefix, v)) + .collect::>() + .join("\n")); + + if args.pretty { + ret.push_str(&format!("\n{}printf ' \\e[32mdone\\e[0m\\n'", command_prefix)); + } + }); + + ret + } +} diff --git a/src/main.rs b/src/main.rs index 8af48e3..18bb07a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,11 @@ -use std::path::PathBuf; -use std::io::ErrorKind; -use std::fs::{self, DirEntry}; use std::process::exit; - -use regex::Regex; use clap::Parser; -use path_clean::PathClean; -const START_KEY: &str = "#=mgen_start=#\n"; -const END_KEY: &str = "\n#=mgen_end=#"; +mod build_rule; +mod makefile; +mod rule_list; +use makefile::*; +use rule_list::*; #[derive(Parser, Debug)] struct Args { @@ -35,407 +32,6 @@ struct Args { thread_local! {static ARGS: Args = Args::parse()} -#[derive(Debug)] -#[allow(dead_code)] -enum BuildRuleKind { - Object(T), - Target(T), - Directory(T), - Convenience(T), -} - -#[derive(Debug,Clone)] -struct BuildRule { - target: String, - prerequisites: Vec, - recipe_commands: Vec, -} - - -#[derive(Debug)] -#[allow(dead_code)] -struct Makefile { - path: String, - prologue: String, - mgen_zone: String, - epilogue: String, - include_dirs: Vec, -} - -#[derive(Debug)] -struct RuleList(Vec>); - -impl BuildRule { - fn get_headers_from_c_file(path: &str, makefile: &Makefile) -> Vec { - let mut headers: Vec = Vec::new(); - let file_contents = fs::read_to_string(path).unwrap_or_else(|e| { - panic!("error: could not read file {path}: {e}"); - }); - - let file_path = PathBuf::from(path); - let file_dir = file_path.parent().unwrap(); - - let includes_regex = Regex::new(r#"#include\s+"(.*?)""#).unwrap(); - let caps = includes_regex.captures_iter(&file_contents); - 'fileloop: for (_, [include_file]) in caps.map(|c| c.extract()) { - let path = file_dir.join(PathBuf::from(include_file)).clean(); - match path.exists() { - true => { - headers.push(String::from(path.to_str().unwrap())); - continue; - }, - false => (), - } - - for dir in makefile.include_dirs.iter() { - let dir = PathBuf::from(&dir); - let path = dir.join(PathBuf::from(include_file)).clean(); - match path.exists() { - true => { - headers.push(String::from(path.to_str().unwrap())); - continue 'fileloop; - }, - false => (), - } - } - - let path = file_dir.join(PathBuf::from(include_file)); - headers.push(String::from(path.to_str().unwrap())); - } - - headers - } - fn new_from_c_file(path: &str, makefile: &Makefile) -> BuildRule { - - let mut prerequisites: Vec = Vec::new(); - - prerequisites.push(String::from(path)); - - prerequisites.append(&mut BuildRule::get_headers_from_c_file(path, makefile)); - - prerequisites.push(String::from("| $(OBJDIR)")); - - let file_path = PathBuf::from(path); - - let obj_name = file_path.with_extension("o"); - let obj_name = obj_name.file_name().unwrap(); - - let obj_path = PathBuf::from("$(OBJDIR)").join(obj_name); - let obj_path = obj_path.to_str().unwrap(); - - let compile_command = format!("$(CC) $(CFLAGS) $(INCS) -o {} -c {}", obj_path, path); - - BuildRule { target: String::from(obj_path), prerequisites, recipe_commands: vec![compile_command] } - } -} - -impl ToString for BuildRuleKind { - fn to_string(&self) -> String { - let rule = match self { - BuildRuleKind::Object(v) => v, - BuildRuleKind::Target(v) => v, - BuildRuleKind::Directory(v) => v, - BuildRuleKind::Convenience(v) => v, - }; - let mut ret = String::new(); - ret.push_str(&format!("{}: ", rule.target)); - ret.push_str(&rule.prerequisites.join(" ")); - ret.push_str(&format!("\n")); - - ARGS.with(|args| { - let mut command_prefix = "\t"; - let mut special = false; - if args.pretty { - command_prefix = "\t@"; - let verb = match self { - BuildRuleKind::Object(_) => "Building", - BuildRuleKind::Target(_) => "Linking", - BuildRuleKind::Directory(_) => "Creating directory", - BuildRuleKind::Convenience(_) => { - special = true; - if rule.target.contains("clean") { - ret.push_str(&format!("{}printf '\\e[90m:: Cleaning up ...\\e[0m'\n", command_prefix)); - } - if rule.target.contains("run") { - ret.push_str(&format!("{}printf '\\e[34m:: Running\\e[0m \\e[1m%s\\e[0m \\e[34m...\\e[0m\\n' $(TARGET)\n", command_prefix)); - } - "" - }, - }; - if !special { - ret.push_str(&format!("{}printf '\\e[90m:: {}\\e[0m \\e[1m%s\\e[0m \\e[90m...\\e[0m' {}\n", command_prefix, verb, - (PathBuf::from(&rule.target)).file_name().unwrap().to_str().unwrap())); - } - } - - ret.push_str(&rule.recipe_commands.iter() - .map(|v| format!("{}{}", command_prefix, v)) - .collect::>() - .join("\n")); - - if args.pretty { - ret.push_str(&format!("\n{}printf ' \\e[32mdone\\e[0m\\n'", command_prefix)); - } - }); - - ret - } -} - -impl Makefile { - fn new_from_path(path: &str) -> Makefile { - let contents = match fs::read_to_string(path) { - Ok(v) => v, - Err(e) => match e.kind() { - // doesn't matter if the file doesn't exist, it will be created - // later. - ErrorKind::NotFound => return Makefile { - path: String::from(path), prologue: String::from(""), - mgen_zone: String::from(""), epilogue: String::from(""), - include_dirs: vec![String::from("")] }, - _ => panic!("error: cannot read makefile: {e}") - } - }; - - let contains_start = contents.find(START_KEY).is_some(); - let contains_end = contents.find(END_KEY).is_some(); - - if (contains_start && !contains_end) || (!contains_start && - contains_end) { - panic!("error: makefile contains one mgen guard but not the other. cannot continue."); - } - - if !(contains_start && contains_end) { - let include_dirs = Makefile::get_include_dirs(&contents); - return Makefile { path: String::from(path), prologue: contents, - mgen_zone: String::from(""), epilogue: String::from(""), include_dirs }; - } - - // contains the part before the start key and everything after - let pre_and_after: Vec<&str> = contents.split(START_KEY).collect(); - // contains the space between start and end keys and also the space after - let gen_and_post: Vec<&str> = pre_and_after[1].split(END_KEY).collect(); - - let include_dirs = Makefile::get_include_dirs(&contents); - - Makefile { - path: String::from(path), - prologue: String::from(pre_and_after[0]), - mgen_zone: String::from(gen_and_post[0]), - epilogue: String::from(gen_and_post[1]), - include_dirs - } - } - - fn get_include_dirs(search: &String) -> Vec { - let mut include_dirs: Vec = Vec::new(); - - let makefile_regex = Regex::new(r"INCS\s*=\s*(.*)").unwrap(); - let caps = makefile_regex.captures_iter(search); - let dirlist = match caps.map(|c| c.extract()).last() { - Some((_, [v])) => v, - None => { - return include_dirs - } - , - }; - - - let incdir_regex = Regex::new(r"\s*-I\s*(.*?)(?:[\s]|$)").unwrap(); - let caps = incdir_regex.captures_iter(&dirlist); - for (_, [dir]) in caps.map(|c| c.extract()) { - include_dirs.push(String::from(dir)); - } - - - include_dirs - } - - fn contains_builddir_def(&self) -> bool { - let re = Regex::new(r"\s+BUILDDIR = ").unwrap(); - re.is_match(&(self.prologue)) || re.is_match(&(self.epilogue)) - } - - fn contains_objdir_def(&self) -> bool { - let re = Regex::new(r"\s+OBJDIR = ").unwrap(); - re.is_match(&(self.prologue)) || re.is_match(&(self.epilogue)) - } - - fn write_new_generated_content(&self, generated_content: String) -> std::io::Result<()> { - let content = [&self.prologue, START_KEY, &generated_content, END_KEY, &self.epilogue].concat(); - - fs::write(&self.path, &content)?; - - Ok(()) - } -} - -impl ToString for RuleList { - fn to_string(&self) -> String { - let targets = &self.get_target_rules().iter().map(|v| v.to_string()).collect::>().join("\n"); - let objects = &self.get_object_rules().iter().map(|v| v.to_string()).collect::>().join("\n"); - let directories = &self.get_directory_rules().iter().map(|v| v.to_string()).collect::>().join("\n"); - let conveniences = &self.get_convenience_rules().iter().map(|v| v.to_string()).collect::>().join("\n"); - - let all = [targets, objects, directories, conveniences]; - let mut nonzero: Vec = Vec::new(); - - for category in all { - if category.len() != 0 { - nonzero.push(category.clone()); - } - } - - nonzero.join("\n\n") - } -} - -impl RuleList { - fn new() -> RuleList{ - RuleList(vec![]) - } - - fn recursively_gen_objects(&mut self, proj_dir_path: &str, makefile: &Makefile) -> std::io::Result<()>{ - let files = fs::read_dir(proj_dir_path)?; - for file in files { - let file = file?; - let is_dir = file.metadata()?.is_dir(); - - // ignore hidden files - if file_is_hidden(&file) { - continue; - } - - let path = file.path(); - - if is_dir { - self.recursively_gen_objects(path.to_str().unwrap(), makefile)?; - } else { - match path.extension() { - Some(v) => { - let v = v.to_str().unwrap(); - if v != "c" { - continue; - } - let path = path.to_str().unwrap(); - self.0.push(BuildRuleKind::Object(BuildRule::new_from_c_file(path, makefile))); - }, - None => { - continue; - }, - } - } - } - Ok(()) - } - - fn get_obj_targets(&self) -> Vec{ - let mut ret: Vec = Vec::new(); - for rule in &self.0 { - match rule { - BuildRuleKind::Object(r) => ret.push(r.target.clone()), - _ => break, - } - } - ret - } - - fn add_target_rule(&mut self) { - let mut objects = self.get_obj_targets(); - let target_comm = String::from( - format!("$(CC) $(LDFLAGS) $(INCS) -o $(BUILDDIR)/$(TARGET) {} $(LDLIBS)", objects.join(" ")) - ); - - objects.push(String::from("| $(OBJDIR) $(BUILDDIR)")); - let prereqs = objects; - - self.0.push(BuildRuleKind::Target(BuildRule { - target: String::from("$(BUILDDIR)/$(TARGET)"), - prerequisites: prereqs, - recipe_commands: vec![target_comm], - })) - } - - fn add_directory_rule(&mut self, dir: &str) { - self.0.push(BuildRuleKind::Directory(BuildRule { - target: dir.to_string(), - prerequisites: vec![], - recipe_commands: vec![format!("mkdir -p {dir}").to_string()] - })); - } - - fn add_clean_rule(&mut self) { - self.0.push(BuildRuleKind::Convenience(BuildRule { - target: String::from(".PHONY: clean\nclean"), - prerequisites: vec![], - recipe_commands: vec![String::from("rm -r $(OBJDIR)"), String::from("rm -r $(BUILDDIR)")] - })); - } - - fn add_run_rule(&mut self) { - self.0.push(BuildRuleKind::Convenience(BuildRule { - target: String::from(".PHONY: run\nrun"), - prerequisites: vec![String::from("$(BUILDDIR)/$(TARGET)")], - recipe_commands: vec![String::from("./$(BUILDDIR)/$(TARGET)")] - })); - } - - fn get_target_rules(&self) -> Vec> { - let mut ret: Vec> = Vec::new(); - for rule in &self.0 { - match rule { - BuildRuleKind::Target(r) => ret.push(BuildRuleKind::Target(r.clone())), - _ => continue, - } - } - ret - } - - fn get_object_rules(&self) -> Vec> { - let mut ret: Vec> = Vec::new(); - for rule in &self.0 { - match rule { - BuildRuleKind::Object(r) => ret.push(BuildRuleKind::Object(r.clone())), - _ => continue, - } - } - ret - } - - fn get_directory_rules(&self) -> Vec> { - let mut ret: Vec> = Vec::new(); - for rule in &self.0 { - match rule { - BuildRuleKind::Directory(r) => ret.push(BuildRuleKind::Directory(r.clone())), - _ => continue, - } - } - ret - } - - fn get_convenience_rules(&self) -> Vec> { - let mut ret: Vec> = Vec::new(); - for rule in &self.0 { - match rule { - BuildRuleKind::Convenience(r) => ret.push(BuildRuleKind::Convenience(r.clone())), - _ => continue, - } - } - ret - } -} - -fn file_is_hidden(file: &DirEntry) -> bool{ - let file_name = file.file_name(); - let str_name = file_name.to_str().unwrap(); - let first_char = str_name.chars().nth(0).unwrap(); - if first_char == '.' { - true - } else { - false - } -} - fn main() { let mut makefile_name = String::new(); diff --git a/src/makefile.rs b/src/makefile.rs new file mode 100644 index 0000000..d21d5db --- /dev/null +++ b/src/makefile.rs @@ -0,0 +1,104 @@ +use std::{fs, io::ErrorKind}; + +use regex::Regex; + +const START_KEY: &str = "#=mgen_start=#\n"; +const END_KEY: &str = "\n#=mgen_end=#"; + +#[derive(Debug)] +#[allow(dead_code)] +pub struct Makefile { + pub path: String, + pub prologue: String, + pub mgen_zone: String, + pub epilogue: String, + pub include_dirs: Vec, +} + +impl Makefile { + pub fn new_from_path(path: &str) -> Makefile { + let contents = match fs::read_to_string(path) { + Ok(v) => v, + Err(e) => match e.kind() { + // doesn't matter if the file doesn't exist, it will be created + // later. + ErrorKind::NotFound => return Makefile { + path: String::from(path), prologue: String::from(""), + mgen_zone: String::from(""), epilogue: String::from(""), + include_dirs: vec![String::from("")] }, + _ => panic!("error: cannot read makefile: {e}") + } + }; + + let contains_start = contents.find(START_KEY).is_some(); + let contains_end = contents.find(END_KEY).is_some(); + + if (contains_start && !contains_end) || (!contains_start && + contains_end) { + panic!("error: makefile contains one mgen guard but not the other. cannot continue."); + } + + if !(contains_start && contains_end) { + let include_dirs = Makefile::get_include_dirs(&contents); + return Makefile { path: String::from(path), prologue: contents, + mgen_zone: String::from(""), epilogue: String::from(""), include_dirs }; + } + + // contains the part before the start key and everything after + let pre_and_after: Vec<&str> = contents.split(START_KEY).collect(); + // contains the space between start and end keys and also the space after + let gen_and_post: Vec<&str> = pre_and_after[1].split(END_KEY).collect(); + + let include_dirs = Makefile::get_include_dirs(&contents); + + Makefile { + path: String::from(path), + prologue: String::from(pre_and_after[0]), + mgen_zone: String::from(gen_and_post[0]), + epilogue: String::from(gen_and_post[1]), + include_dirs + } + } + + pub fn get_include_dirs(search: &String) -> Vec { + let mut include_dirs: Vec = Vec::new(); + + let makefile_regex = Regex::new(r"INCS\s*=\s*(.*)").unwrap(); + let caps = makefile_regex.captures_iter(search); + let dirlist = match caps.map(|c| c.extract()).last() { + Some((_, [v])) => v, + None => { + return include_dirs + } + , + }; + + + let incdir_regex = Regex::new(r"\s*-I\s*(.*?)(?:[\s]|$)").unwrap(); + let caps = incdir_regex.captures_iter(&dirlist); + for (_, [dir]) in caps.map(|c| c.extract()) { + include_dirs.push(String::from(dir)); + } + + + include_dirs + } + + pub fn contains_builddir_def(&self) -> bool { + let re = Regex::new(r"\s+BUILDDIR = ").unwrap(); + re.is_match(&(self.prologue)) || re.is_match(&(self.epilogue)) + } + + pub fn contains_objdir_def(&self) -> bool { + let re = Regex::new(r"\s+OBJDIR = ").unwrap(); + re.is_match(&(self.prologue)) || re.is_match(&(self.epilogue)) + } + + pub fn write_new_generated_content(&self, generated_content: String) -> std::io::Result<()> { + let content = [&self.prologue, START_KEY, &generated_content, END_KEY, &self.epilogue].concat(); + + fs::write(&self.path, &content)?; + + Ok(()) + } +} diff --git a/src/rule_list.rs b/src/rule_list.rs new file mode 100644 index 0000000..b3879a5 --- /dev/null +++ b/src/rule_list.rs @@ -0,0 +1,174 @@ +use std::fs; +use std::fs::DirEntry; + +use crate::build_rule::*; +use crate::makefile::*; + +#[derive(Debug)] +pub struct RuleList(pub Vec>); + +impl RuleList { + pub fn new() -> RuleList{ + RuleList(vec![]) + } + + pub fn recursively_gen_objects(&mut self, proj_dir_path: &str, makefile: &Makefile) -> std::io::Result<()>{ + let files = fs::read_dir(proj_dir_path)?; + for file in files { + let file = file?; + let is_dir = file.metadata()?.is_dir(); + + // ignore hidden files + if file_is_hidden(&file) { + continue; + } + + let path = file.path(); + + if is_dir { + self.recursively_gen_objects(path.to_str().unwrap(), makefile)?; + } else { + match path.extension() { + Some(v) => { + let v = v.to_str().unwrap(); + if v != "c" { + continue; + } + let path = path.to_str().unwrap(); + self.0.push(BuildRuleKind::Object(BuildRule::new_from_c_file(path, makefile))); + }, + None => { + continue; + }, + } + } + } + Ok(()) + } + + pub fn get_obj_targets(&self) -> Vec{ + let mut ret: Vec = Vec::new(); + for rule in &self.0 { + match rule { + BuildRuleKind::Object(r) => ret.push(r.target.clone()), + _ => break, + } + } + ret + } + + pub fn add_target_rule(&mut self) { + let mut objects = self.get_obj_targets(); + let target_comm = String::from( + format!("$(CC) $(LDFLAGS) $(INCS) -o $(BUILDDIR)/$(TARGET) {} $(LDLIBS)", objects.join(" ")) + ); + + objects.push(String::from("| $(OBJDIR) $(BUILDDIR)")); + let prereqs = objects; + + self.0.push(BuildRuleKind::Target(BuildRule { + target: String::from("$(BUILDDIR)/$(TARGET)"), + prerequisites: prereqs, + recipe_commands: vec![target_comm], + })) + } + + pub fn add_directory_rule(&mut self, dir: &str) { + self.0.push(BuildRuleKind::Directory(BuildRule { + target: dir.to_string(), + prerequisites: vec![], + recipe_commands: vec![format!("mkdir -p {dir}").to_string()] + })); + } + + pub fn add_clean_rule(&mut self) { + self.0.push(BuildRuleKind::Convenience(BuildRule { + target: String::from(".PHONY: clean\nclean"), + prerequisites: vec![], + recipe_commands: vec![String::from("rm -r $(OBJDIR)"), String::from("rm -r $(BUILDDIR)")] + })); + } + + pub fn add_run_rule(&mut self) { + self.0.push(BuildRuleKind::Convenience(BuildRule { + target: String::from(".PHONY: run\nrun"), + prerequisites: vec![String::from("$(BUILDDIR)/$(TARGET)")], + recipe_commands: vec![String::from("./$(BUILDDIR)/$(TARGET)")] + })); + } + + pub fn get_target_rules(&self) -> Vec> { + let mut ret: Vec> = Vec::new(); + for rule in &self.0 { + match rule { + BuildRuleKind::Target(r) => ret.push(BuildRuleKind::Target(r.clone())), + _ => continue, + } + } + ret + } + + pub fn get_object_rules(&self) -> Vec> { + let mut ret: Vec> = Vec::new(); + for rule in &self.0 { + match rule { + BuildRuleKind::Object(r) => ret.push(BuildRuleKind::Object(r.clone())), + _ => continue, + } + } + ret + } + + pub fn get_directory_rules(&self) -> Vec> { + let mut ret: Vec> = Vec::new(); + for rule in &self.0 { + match rule { + BuildRuleKind::Directory(r) => ret.push(BuildRuleKind::Directory(r.clone())), + _ => continue, + } + } + ret + } + + pub fn get_convenience_rules(&self) -> Vec> { + let mut ret: Vec> = Vec::new(); + for rule in &self.0 { + match rule { + BuildRuleKind::Convenience(r) => ret.push(BuildRuleKind::Convenience(r.clone())), + _ => continue, + } + } + ret + } +} + +fn file_is_hidden(file: &DirEntry) -> bool{ + let file_name = file.file_name(); + let str_name = file_name.to_str().unwrap(); + let first_char = str_name.chars().nth(0).unwrap(); + if first_char == '.' { + true + } else { + false + } +} + +impl ToString for RuleList { + fn to_string(&self) -> String { + let targets = &self.get_target_rules().iter().map(|v| v.to_string()).collect::>().join("\n"); + let objects = &self.get_object_rules().iter().map(|v| v.to_string()).collect::>().join("\n"); + let directories = &self.get_directory_rules().iter().map(|v| v.to_string()).collect::>().join("\n"); + let conveniences = &self.get_convenience_rules().iter().map(|v| v.to_string()).collect::>().join("\n"); + + let all = [targets, objects, directories, conveniences]; + let mut nonzero: Vec = Vec::new(); + + for category in all { + if category.len() != 0 { + nonzero.push(category.clone()); + } + } + + nonzero.join("\n\n") + } +}