Skip to content

Commit 494551d

Browse files
committed
Improve CLI, use std::io::Lines
1 parent 238b76c commit 494551d

File tree

5 files changed

+143
-50
lines changed

5 files changed

+143
-50
lines changed

lines-of-code/Cargo.lock

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

lines-of-code/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ test = "spectra check README.md 'target/debug/lines-of-code --rpc --interactive'
2020

2121
[dependencies]
2222
glob = "0.3.3"
23+
json-builder-macro = "0.0.1"

lines-of-code/lib.rs

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1+
use std::io::{BufRead, BufReader};
2+
13
#[derive(Default, Debug)]
24
pub struct RustSection {
3-
pub lines: usize,
5+
pub package_name: String,
6+
pub lines: u64,
47
/// lines dedicated to tests.
58
/// these can be within the `tests` folder or surrounded by `#[cfg(test)]`
6-
pub test_lines: usize,
9+
pub test_lines: u64,
710
/// lines dedicated to examples
8-
pub example_lines: usize,
9-
enums: usize,
10-
structs: usize,
11-
type_aliases: usize,
12-
fns: usize,
13-
impls: usize,
14-
variables: usize,
15-
comments: usize,
16-
pub modules: usize,
11+
pub example_lines: u64,
12+
enums: u64,
13+
structs: u64,
14+
type_aliases: u64,
15+
fns: u64,
16+
impls: u64,
17+
variables: u64,
18+
comments: u64,
19+
pub modules: u64,
1720
}
1821

1922
impl std::ops::AddAssign for RustSection {
@@ -30,6 +33,7 @@ impl std::ops::AddAssign for RustSection {
3033
lines,
3134
modules,
3235
type_aliases,
36+
package_name: _,
3337
} = other;
3438
self.enums += enums;
3539
self.structs += structs;
@@ -45,14 +49,15 @@ impl std::ops::AddAssign for RustSection {
4549
}
4650
}
4751

48-
pub fn measure(on: &str) -> RustSection {
52+
pub fn measure<R: std::io::Read>(on: BufReader<R>) -> RustSection {
4953
let mut is_test = false;
50-
let mut test_indent: Option<&str> = None;
54+
let mut test_indent: Option<String> = None;
5155
let mut code = RustSection::default();
5256
let mut is_multiline_comment = false;
5357

5458
for line in on.lines() {
55-
if let Some(indent) = test_indent {
59+
let line = line.unwrap();
60+
if let Some(ref indent) = test_indent {
5661
if let Some(rest) = line.strip_prefix(indent)
5762
&& rest == "}"
5863
{
@@ -78,7 +83,7 @@ pub fn measure(on: &str) -> RustSection {
7883
if std::mem::take(&mut is_test) {
7984
if line.trim_end().ends_with('{') {
8085
let indent_count = line.len() - line.trim_start().len();
81-
test_indent = Some(&line[..indent_count]);
86+
test_indent = Some(line[..indent_count].to_owned());
8287
}
8388
continue;
8489
}
@@ -147,6 +152,7 @@ pub fn measure(on: &str) -> RustSection {
147152
impl RustSection {
148153
pub fn to_json(&self) -> String {
149154
let Self {
155+
package_name,
150156
enums,
151157
structs,
152158
fns,
@@ -159,9 +165,28 @@ impl RustSection {
159165
example_lines,
160166
type_aliases,
161167
} = self;
162-
format!(
163-
r#"{{"modules":{modules},"lines":{lines},"variables":{variables},"type_aliases":{type_aliases},"example_lines":{example_lines},"comments":{comments},"enums":{enums},"structs":{structs},"fns":{fns},"impls":{impls},"test_lines":{test_lines}}}"#
164-
)
168+
169+
let mut buf = String::new();
170+
{
171+
let mut builder = json_builder_macro::Builder::new(&mut buf);
172+
if !package_name.is_empty() {
173+
builder.add("name", package_name.as_str());
174+
}
175+
176+
builder.add("modules", *modules);
177+
builder.add("lines", *lines);
178+
builder.add("variables", *variables);
179+
builder.add("type_aliases", *type_aliases);
180+
builder.add("comments", *comments);
181+
builder.add("enums", *enums);
182+
builder.add("structs", *structs);
183+
builder.add("functions", *fns);
184+
builder.add("implementations", *impls);
185+
builder.add("test_lines", *test_lines);
186+
builder.add("example_lines", *example_lines);
187+
builder.end();
188+
}
189+
buf
165190
}
166191

167192
pub fn debug(&self) {
@@ -177,6 +202,7 @@ impl RustSection {
177202
modules,
178203
type_aliases,
179204
example_lines,
205+
package_name: _,
180206
} = self;
181207
if *lines > 0 {
182208
println!("lines: {lines}");

lines-of-code/main.rs

Lines changed: 90 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,97 @@
11
use lines_of_code::{RustSection, measure};
2+
use std::io::{BufRead, BufReader};
23

34
fn main() {
4-
let arg = std::env::args().nth(1);
5-
6-
if let Some("--interactive") = arg.as_deref() {
7-
run_interactive()
8-
} else {
9-
let mut code = RustSection::default();
10-
11-
let pattern = arg.as_deref().unwrap_or("**/*.rs");
12-
13-
let files = glob::glob(pattern)
14-
.unwrap()
15-
.filter_map(Result::ok)
16-
.filter(|path| path.is_file());
17-
18-
for path in files {
19-
code.modules += 1;
20-
let content = std::fs::read_to_string(&path).unwrap();
21-
let file = measure(&content);
22-
23-
let path_str = path.as_os_str().to_str();
24-
if path_str.is_some_and(|path| path.contains("examples")) {
25-
code.example_lines += file.lines;
26-
} else if path_str.is_some_and(|path| path.contains("tests")) {
27-
code.test_lines += file.lines;
28-
} else {
29-
code += file;
30-
}
5+
let mut arguments = std::env::args().skip(1);
6+
7+
let first_argument = arguments.next();
8+
let endpoint = first_argument.as_deref().unwrap_or("**/*.rs");
9+
10+
match endpoint {
11+
// For testing
12+
"--interactive" => run_interactive(),
13+
"info" | "--help" => {
14+
let run_id = option_env!("GITHUB_RUN_ID");
15+
let date = option_env!("GIT_LAST_COMMIT").unwrap_or_default();
16+
let after = run_id
17+
.map(|commit| format!(" (commit {commit} {date})"))
18+
.unwrap_or_default();
19+
20+
eprintln!("lines-of-code{after}");
21+
eprintln!("count lines of code and other items in Rust projects");
3122
}
23+
argument => {
24+
let mut pattern = argument;
25+
26+
let mut count = RustSection::default();
27+
let mut trace = false;
28+
29+
// WIP
30+
for argument in arguments {
31+
if let "--trace" = argument.as_str() {
32+
trace = true;
33+
} else {
34+
eprintln!("unknown argument {argument:?}");
35+
}
36+
}
37+
38+
let p;
39+
if let Ok(entry) = std::fs::metadata(pattern)
40+
&& entry.is_dir()
41+
{
42+
p = format!("{pattern}/**");
43+
pattern = &p;
44+
}
45+
46+
{
47+
let path = pattern.split_once('*').unwrap_or((pattern, "")).0;
48+
let path = path.rsplit_once(['\\', '/']).unwrap_or((path, "")).0;
49+
let path = if path.ends_with("src") {
50+
&path[..path.len() - 4]
51+
} else {
52+
path
53+
};
3254

33-
println!("{json}", json = code.to_json());
55+
if let Ok(cargo_toml) =
56+
std::fs::File::open(std::path::Path::new(path).join("Cargo.toml"))
57+
{
58+
for line in BufReader::new(cargo_toml).lines().map_while(Result::ok) {
59+
if let Some(item) = line.strip_prefix("name") {
60+
let (_, item) = item.split_once('"').unwrap();
61+
let (item, _) = item.rsplit_once('"').unwrap();
62+
count.package_name = item.to_owned();
63+
break;
64+
}
65+
}
66+
}
67+
}
68+
69+
let files = glob::glob(pattern)
70+
.unwrap()
71+
.filter_map(Result::ok)
72+
.filter(|path| path.is_file());
73+
74+
for path in files {
75+
if trace {
76+
eprintln!("Reading {path:?}");
77+
}
78+
count.modules += 1;
79+
80+
let content = std::fs::File::open(&path).unwrap();
81+
let inner_count = measure(BufReader::new(content));
82+
83+
let path_str = path.as_os_str().to_str();
84+
if path_str.is_some_and(|path| path.contains("examples")) {
85+
count.example_lines += inner_count.lines;
86+
} else if path_str.is_some_and(|path| path.contains("tests")) {
87+
count.test_lines += inner_count.lines;
88+
} else {
89+
count += inner_count;
90+
}
91+
}
92+
93+
println!("{json}", json = count.to_json());
94+
}
3495
}
3596
}
3697

@@ -50,9 +111,7 @@ fn run_interactive() {
50111
}
51112

52113
if line == "end" {
53-
let output = String::from_utf8_lossy(&buf);
54-
55-
measure(&output).debug();
114+
measure(BufReader::new(buf.as_slice())).debug();
56115

57116
println!("end");
58117
buf.clear();

lines-of-code/tests/specification.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#[test]
22
fn main() -> std::process::ExitCode {
33
let output = std::process::Command::new("spectra")
4-
.arg("check")
4+
.arg("test")
55
.arg("README.md")
66
.arg("./target/debug/lines-of-code --rpc --interactive")
77
.status()

0 commit comments

Comments
 (0)