grep_table_converter/
generator.rs

1use anyhow::Result;
2use anyhow::anyhow;
3
4#[derive(Debug, PartialEq)]
5pub enum Mode {
6    CSV,
7    MARKDOWN,
8    TEXTILE,
9}
10
11impl Mode {
12    fn header(&self) -> String {
13        match self {
14            Mode::CSV => "file_name,line_num,content\n".to_string(),
15            Mode::MARKDOWN => "| file_name | line_num | content |\n| --- | --- | --- |\n".to_string(),
16            Mode::TEXTILE => "|file_name|line_num|content|\n".to_string(),
17        }
18    }
19
20    pub fn from(input: &str) -> Result<Mode> {
21        match input.to_lowercase().as_str() {
22            "csv" => Ok(Mode::CSV),
23            "markdown" => Ok(Mode::MARKDOWN),
24            "textile" => Ok(Mode::TEXTILE),
25            _ => Err(anyhow!("mode must be csv or markdown or textile.")),
26        }
27    }
28
29    pub fn extension(&self) -> &str {
30        match self {
31            Mode::CSV => ".csv",
32            Mode::MARKDOWN => ".md",
33            Mode::TEXTILE => ".textile",
34        }
35    }
36}
37
38#[derive(Debug)]
39struct Line<'a> {
40    file_path: &'a str,
41    line_num: &'a str,
42    remaining: &'a str,
43}
44
45impl Line<'_> {
46    fn format(&self, mode: &Mode) -> String {
47        match mode {
48            Mode::CSV => format!("{},{},{}\n", self.file_path, self.line_num, self.remaining),
49            Mode::MARKDOWN => format!("| {} | {} | {} |\n", self.file_path, self.line_num, self.remaining),
50            Mode::TEXTILE => format!("|{}|{}|{}|\n", self.file_path, self.line_num, self.remaining),
51        }
52    }
53}
54
55pub fn generate_table(content: &str, mode: &Mode) -> Result<String> {
56    let mut result = String::from(mode.header());
57    for line in content.lines() {
58        let splitted_line: Vec<&str> = line.split(":").collect();
59
60        if splitted_line.len() < 3 {
61            return Err(anyhow!("Invalid format.\nexpected: [file path]:[line number]:[code]\ngiven: {}", &content));
62        }
63
64        let line_data = Line {
65            file_path: &splitted_line[0],
66            line_num: &splitted_line[1],
67            remaining: &splitted_line[2..].join(":").to_string(),
68        };
69
70        result += line_data.format(mode).as_str();
71    }
72    Ok(result)
73}
74
75#[cfg(test)]
76mod test {
77    use super::*;
78
79    #[test]
80    fn export_mode_enum() {
81        let mode = Mode::MARKDOWN;
82        assert_eq!("MARKDOWN", format!("{:?}", &mode))
83    }
84
85    #[test]
86    fn mode_from_valid_string() {
87        let mode1 = Mode::from("CsV");
88        let mode2 = Mode::from("markDoWN");
89        let mode3 = Mode::from("TEXtile");
90        if let Ok(mode) = mode1 {
91            assert_eq!(Mode::CSV, mode);
92        } else {
93            assert!(false);
94        }
95        if let Ok(mode) = mode2 {
96            assert_eq!(Mode::MARKDOWN, mode);
97        } else {
98            assert!(false);
99        }
100        if let Ok(mode) = mode3 {
101            assert_eq!(Mode::TEXTILE, mode);
102        } else {
103            assert!(false);
104        }
105    }
106
107    #[test]
108    fn mode_fron_invalid_string() {
109        let mode = Mode::from("invalid");
110        if let Ok(_) = mode {
111            assert!(false);
112        } else {
113            assert!(true);
114        }
115    }
116
117    #[test]
118    fn get_extension() {
119        let csv_mode = Mode::CSV;
120        let md_mode = Mode::MARKDOWN;
121        let txtile_mode = Mode::TEXTILE;
122
123        assert_eq!(".csv", csv_mode.extension());
124        assert_eq!(".md", md_mode.extension());
125        assert_eq!(".textile", txtile_mode.extension());
126    }
127
128    #[test]
129    fn debug_print_line() {
130        let line = Line { file_path: "src/test.rs", line_num: "124", remaining: "this is test code." };
131        assert_eq!("Line { file_path: \"src/test.rs\", line_num: \"124\", remaining: \"this is test code.\" }", format!("{:?}", line));
132    }
133
134    #[test]
135    fn csv_result() {
136        let mode = Mode::CSV;
137        let content = r#"test.rs:155:this is test
138test.rs:201:TestCrate::test_method();
139modules/hoge_module.rs:14:println!("this is test String.");"#;
140        let expect = r#"file_name,line_num,content
141test.rs,155,this is test
142test.rs,201,TestCrate::test_method();
143modules/hoge_module.rs,14,println!("this is test String.");
144"#;
145        assert_eq!(expect, generate_table(&content, &mode).unwrap());
146    }
147
148    #[test]
149    fn markdown_result() {
150        let mode = Mode::MARKDOWN;
151        let content = r#"test.rs:155:this is test
152test.rs:201:TestCrate::test_method();
153modules/hoge_module.rs:14:println!("this is test String.");"#;
154        let expect = r#"| file_name | line_num | content |
155| --- | --- | --- |
156| test.rs | 155 | this is test |
157| test.rs | 201 | TestCrate::test_method(); |
158| modules/hoge_module.rs | 14 | println!("this is test String."); |
159"#;
160        assert_eq!(expect, generate_table(&content, &mode).unwrap());
161    }
162
163    #[test]
164    fn textile_result() {
165        let mode = Mode::TEXTILE;
166        let content = r#"test.rs:155:this is test
167test.rs:201:TestCrate::test_method();
168modules/hoge_module.rs:14:println!("this is test String.");"#;
169        let expect = r#"|file_name|line_num|content|
170|test.rs|155|this is test|
171|test.rs|201|TestCrate::test_method();|
172|modules/hoge_module.rs|14|println!("this is test String.");|
173"#;
174        assert_eq!(expect, generate_table(&content, &mode).unwrap());
175    }
176
177    #[test]
178    fn invalid_format() {
179        let mode = Mode::CSV;
180        let content = "This is a test for invalid format error.";
181        let expected_errmsg = format!("Invalid format.\nexpected: [file path]:[line number]:[code]\ngiven: {}", &content);
182        if let Err(e) = generate_table(&content, &mode) {
183            assert_eq!(expected_errmsg, format!("{:?}", e));
184        } else {
185            assert!(false, "result should be error");
186        }
187    }
188}