diff --git a/Cargo.lock b/Cargo.lock index 0b276b01..baa226fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -75,13 +81,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "time 0.1.45", "wasm-bindgen", @@ -377,6 +383,7 @@ version = "0.5.0" dependencies = [ "regex", "skim", + "uuid", ] [[package]] @@ -477,9 +484,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "link-cplusplus" @@ -548,16 +555,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.15" @@ -872,6 +869,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "vte" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 325f1a72..a6d10743 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] regex = "1.7.1" skim = "0.10.4" +uuid = { version = "1.4.1", features = ["serde", "v4"] } diff --git a/Makefile b/Makefile index 2cd9ae6b..218c0250 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ echo-test: @echo good test : # run test - cargo test + cargo nextest run run: @cargo run diff --git a/src/misc.rs b/src/misc.rs index c17f817a..4a95a86b 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -1,11 +1,8 @@ use regex::Regex; use skim::prelude::*; -use std::{ - fs::File, - io::{Cursor, Read}, - process, - sync::Arc, -}; +use std::fs::{File, OpenOptions}; +use std::io::Read; +use std::{io::Cursor, process, sync::Arc}; // FIXME rename module @@ -43,54 +40,78 @@ pub fn get_params<'a>() -> (SkimOptions<'a>, Option>> } fn extract_command_from_makefile() -> Result { - let mut files = read_makefile()?; //TODO: こいつの返り値をVecにする - // let contents = read_file_contents(&mut files)?; // TODO: ここで結合する - // let commands = contents_to_commands(contents)?; - // Ok(commands.join("\n")) - Ok(String::new()) // TODO: あとで戻す + let files = get_makefile_file_names()?; + let contents = concat_file_contents(files)?; + let commands = contents_to_commands(contents)?; + Ok(commands.join("\n")) } -// read_makefile returns Makefile and the files named like *.mk -fn read_makefile() -> Result, &'static str> { - let mut makefiles: Vec = Vec::new(); +// get_makefile_file_names returns filenames of Makefile and the files named like *.mk +fn get_makefile_file_names() -> Result, &'static str> { + let mut file_names: Vec = Vec::new(); - // add Makefile to `makefiles` - match File::open("Makefile").map_err(|_| "Makefile not found") { - Ok(f) => makefiles.push(f), + let makefile = "Makefile"; + match File::open(makefile).map_err(|_| "Makefile not found") { Err(err) => return Err(err), + Ok(_) => file_names.push(makefile.to_string()), } // add *.mk to `makefiles` if exist - match std::fs::read_dir(".") { - Ok(entries) => { - for entry in entries { - match entry { - Ok(e) => { - let path = e.path(); - if let Some(ext) = path.extension() { - if ext == "mk" { - match File::open(path) { - Ok(f) => makefiles.push(f), - Err(_) => continue, - } - } - } - } - Err(_) => continue, - } - } - } + let entries = match std::fs::read_dir(".") { Err(_) => return Err("fail to read directory"), + Ok(entries) => entries, + }; + + for entry in entries { + let entry = match entry { + Err(_) => continue, + Ok(e) => e, + }; + + let path = entry.path(); + let ext = match path.extension() { + None => continue, + Some(ext) => ext, + }; + if ext != "mk" { + continue; + } + + let file_name = match entry.file_name().into_string() { + // c.f. https://zenn.dev/suzuki_hoge/books/2023-03-rust-strings-8868f207b3ed18/viewer/4-os-string-and-os-str + Err(entry) => panic!("file name is not utf-8: {:?}", entry), + Ok(f) => f, + }; + file_names.push(file_name); } - Ok(makefiles) + Ok(file_names) } -fn read_file_contents(file: &mut File) -> Result { +fn concat_file_contents(file_paths: Vec) -> Result { let mut contents = String::new(); - file.read_to_string(&mut contents) - .map(|_| contents) - .map_err(|_| "fail to read Makefile contents") + for path in file_paths { + let mut content = String::new(); + let mut file = match OpenOptions::new().read(true).open(path) { + Err(_) => return Err("fail to open file"), + Ok(f) => f, + }; + + match file.read_to_string(&mut content) { + Err(e) => { + print!("fail to read file: {:?}", e); + return Err("fail to read file"); + } + Ok(_) => { + if !contents.is_empty() { + contents += "\n"; + } + + contents += &content; + } + } + } + Ok(contents) } fn contents_to_commands(contents: String) -> Result, &'static str> { @@ -125,10 +146,81 @@ fn line_to_command(line: String) -> Option { #[cfg(test)] mod test { - use std::str::FromStr; + use std::{io::Write, str::FromStr}; + use uuid::Uuid; use super::*; + #[test] + fn concat_file_contents_test() { + struct Case { + file_contents: Vec<&'static str>, + expect: Result<&'static str, &'static str>, + } + let cases = vec![Case { + file_contents: vec![ + "\ +.PHONY: test-1 +test-1: + @cargo run", + "\ +.PHONY: test-2 +test-2: + @cargo run", + ], + expect: Ok("\ +.PHONY: test-1 +test-1: + @cargo run +.PHONY: test-2 +test-2: + @cargo run"), + }]; + + for case in cases { + let in_file_names: Vec = case + .file_contents + .iter() + .map(|content| { + let random_file_name = Uuid::new_v4().to_string(); + test_file_from_content(random_file_name, content) + }) + .collect(); + + assert_eq!( + case.expect.map(|e| e.to_string()), + concat_file_contents(in_file_names) + ); + } + } + + fn test_file_from_content(file_name: String, content: &'static str) -> String { + let tmp_dir = std::env::temp_dir(); + let file_name = file_name + ".mk"; + let file_path = tmp_dir.join(&file_name); + + let mut file = match OpenOptions::new() + .create(true) + .write(true) + .read(true) + .append(true) + .open(&file_path) + { + Err(err) => panic!("fail to create file: {:?}", err), + Ok(file) => file, + }; + + match file.write_all(content.as_bytes()) { + Err(e) => { + print!("fail to write file: {:?}", e); + process::exit(1); + } + Ok(_) => {} + } + + file_path.to_path_buf().to_str().unwrap().to_string() + } + #[test] fn contents_to_commands_test() { struct Case { @@ -176,12 +268,12 @@ build: ]; for case in cases { - let a = case.expect.map(|x| { + let expect = case.expect.map(|x| { x.iter() .map(|y| String::from_str(y).unwrap()) .collect::>() }); - assert_eq!(a, contents_to_commands(case.contents.to_string())); + assert_eq!(expect, contents_to_commands(case.contents.to_string())); } }