Skip to content

Commit 7106bfa

Browse files
authored
Add more output formats (#160)
* Add more output formats Add cbor, json, toml and yaml as additional output formats * Fix typo
1 parent fb8d481 commit 7106bfa

8 files changed

Lines changed: 1207 additions & 1042 deletions

File tree

Cargo.lock

Lines changed: 759 additions & 711 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ petgraph = "^0.5"
2424
phf = { version = "^0.8", features = ["macros"] }
2525
regex = "^1.1"
2626
serde = "^1.0"
27+
serde_cbor = "^0.11"
2728
serde_json = "^1.0"
2829
termcolor = "^1.0"
2930
tree-sitter = "^0.6"
31+
serde_yaml = "^0.8"
32+
toml = "^0.5"
33+
walkdir = "^2.2"
3034

3135
[dev-dependencies]
3236
pretty_assertions = "^0.6"

rust-code-analysis-cli/src/main.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use crossbeam::channel::{Receiver, Sender};
99
use crossbeam::crossbeam_channel::unbounded;
1010
use globset::{Glob, GlobSet, GlobSetBuilder};
1111
use std::collections::{hash_map, HashMap};
12+
use std::fmt;
1213
use std::path::PathBuf;
14+
use std::str::FromStr;
1315
use std::sync::{Arc, Mutex};
1416
use std::{process, thread};
1517
use walkdir::{DirEntry, WalkDir};
@@ -26,6 +28,7 @@ struct Config {
2628
count_filter: Vec<String>,
2729
function: bool,
2830
metrics: bool,
31+
output_format: Option<Format>,
2932
output: String,
3033
pretty: bool,
3134
line_start: Option<usize>,
@@ -80,6 +83,7 @@ fn act_on_file(language: Option<LANG>, path: PathBuf, cfg: &Config) -> std::io::
8083
} else if cfg.metrics {
8184
let cfg = MetricsCfg {
8285
path,
86+
output_format: cfg.output_format.clone(),
8387
pretty: cfg.pretty,
8488
output_path: if cfg.output.is_empty() {
8589
None
@@ -217,6 +221,17 @@ fn explore(
217221
all_files
218222
}
219223

224+
fn parse_or_exit<T>(s: &str) -> T
225+
where
226+
T: FromStr,
227+
T::Err: fmt::Display,
228+
{
229+
T::from_str(s).unwrap_or_else(|e| {
230+
eprintln!("Error:\n{}", e);
231+
process::exit(1);
232+
})
233+
}
234+
220235
fn main() {
221236
let matches = App::new("code-analysis")
222237
.version(crate_version!())
@@ -303,13 +318,21 @@ fn main() {
303318
.takes_value(true),
304319
)
305320
.arg(
306-
Arg::with_name("type")
321+
Arg::with_name("language_type")
307322
.help("Language type")
308-
.short("t")
309-
.long("type")
323+
.short("l")
324+
.long("language-type")
310325
.default_value("")
311326
.takes_value(true),
312327
)
328+
.arg(
329+
Arg::with_name("output_format")
330+
.help("Output metrics as different formats")
331+
.short("O")
332+
.long("output-format")
333+
.possible_values(Format::all())
334+
.takes_value(true),
335+
)
313336
.arg(
314337
Arg::with_name("pretty")
315338
.help("Dump a pretty json file")
@@ -416,7 +439,7 @@ fn main() {
416439
None
417440
};
418441
let metrics = matches.is_present("metrics");
419-
let typ = matches.value_of("type").unwrap();
442+
let typ = matches.value_of("language_type").unwrap();
420443
let preproc_value = matches.value_of("preproc").unwrap();
421444
let (preproc_lock, preproc) = if !preproc_value.is_empty() {
422445
let path = PathBuf::from(preproc_value);
@@ -436,6 +459,9 @@ fn main() {
436459
(None, None)
437460
};
438461

462+
let output_format = matches
463+
.value_of("output_format")
464+
.map(parse_or_exit::<Format>);
439465
let pretty = matches.is_present("pretty");
440466
let output = matches.value_of("output").unwrap().to_string();
441467
let output_is_dir = PathBuf::from(output.clone()).is_dir();
@@ -475,6 +501,7 @@ fn main() {
475501
count_filter,
476502
function,
477503
metrics,
504+
output_format,
478505
pretty,
479506
output: output.clone(),
480507
line_start,

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
extern crate lazy_static;
66
#[macro_use]
77
extern crate serde;
8+
extern crate serde_cbor;
89
#[cfg_attr(test, macro_use)]
910
extern crate serde_json;
11+
extern crate serde_yaml;
12+
extern crate toml;
1013

1114
#[macro_use]
1215
mod macros;

src/output/dump_formats.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use regex::Regex;
2+
use std::fs::File;
3+
use std::io::Write;
4+
use std::io::{Error, ErrorKind};
5+
use std::path::PathBuf;
6+
use std::str::FromStr;
7+
8+
use crate::spaces::FuncSpace;
9+
10+
#[derive(Debug, Clone)]
11+
pub enum Format {
12+
Cbor,
13+
Json,
14+
Toml,
15+
Yaml,
16+
}
17+
18+
impl Format {
19+
pub fn all() -> &'static [&'static str] {
20+
&["cbor", "json", "toml", "yaml"]
21+
}
22+
}
23+
24+
impl FromStr for Format {
25+
type Err = String;
26+
27+
fn from_str(format: &str) -> Result<Self, Self::Err> {
28+
match format {
29+
"cbor" => Ok(Format::Cbor),
30+
"json" => Ok(Format::Json),
31+
"toml" => Ok(Format::Toml),
32+
"yaml" => Ok(Format::Yaml),
33+
format => Err(format!("{:?} is not a supported format", format)),
34+
}
35+
}
36+
}
37+
38+
pub(crate) fn dump_formats(
39+
space: &FuncSpace,
40+
path: &PathBuf,
41+
output_path: &Option<PathBuf>,
42+
output_format: Format,
43+
pretty: bool,
44+
) -> std::io::Result<()> {
45+
if output_path.is_none() {
46+
let stdout = std::io::stdout();
47+
let mut stdout = stdout.lock();
48+
49+
match output_format {
50+
Format::Cbor => Err(Error::new(
51+
ErrorKind::Other,
52+
"Cbor format cannot be printed to stdout",
53+
)),
54+
Format::Json => {
55+
let json_data = if pretty {
56+
serde_json::to_string_pretty(&space).unwrap()
57+
} else {
58+
serde_json::to_string(&space).unwrap()
59+
};
60+
write!(stdout, "{}", json_data)
61+
}
62+
Format::Toml => {
63+
let toml_data = if pretty {
64+
toml::to_string_pretty(&space).unwrap()
65+
} else {
66+
toml::to_string(&space).unwrap()
67+
};
68+
write!(stdout, "{}", toml_data)
69+
}
70+
Format::Yaml => write!(stdout, "{}", serde_yaml::to_string(&space).unwrap()),
71+
}
72+
} else {
73+
let format_ext = match output_format {
74+
Format::Cbor => ".cbor",
75+
Format::Json => ".json",
76+
Format::Toml => ".toml",
77+
Format::Yaml => ".yml",
78+
};
79+
80+
let output_path = output_path.as_ref().unwrap();
81+
82+
let mut file = path.as_path().file_name().unwrap().to_os_string();
83+
file.push(format_ext);
84+
85+
let mut format_path = output_path.clone();
86+
format_path.push(file);
87+
88+
if format_path.as_path().exists() {
89+
let mut new_filename = path.to_str().unwrap().to_string();
90+
let re = Regex::new(r"[\\:/]").unwrap();
91+
new_filename = re.replace_all(&new_filename, "_").to_string();
92+
new_filename.push_str(format_ext);
93+
format_path.pop();
94+
format_path.push(new_filename);
95+
}
96+
97+
let mut format_file = File::create(format_path)?;
98+
match output_format {
99+
Format::Cbor => serde_cbor::to_writer(format_file, &space)
100+
.map_err(|e| Error::new(ErrorKind::Other, e.to_string())),
101+
Format::Json => {
102+
if pretty {
103+
serde_json::to_writer_pretty(format_file, &space)
104+
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
105+
} else {
106+
serde_json::to_writer(format_file, &space)
107+
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
108+
}
109+
}
110+
Format::Toml => {
111+
let toml_data = if pretty {
112+
toml::to_string_pretty(&space).unwrap()
113+
} else {
114+
toml::to_string(&space).unwrap()
115+
};
116+
format_file.write_all(toml_data.as_bytes())
117+
}
118+
Format::Yaml => serde_yaml::to_writer(format_file, &space)
119+
.map_err(|e| Error::new(ErrorKind::Other, e.to_string())),
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)