198 lines
6.1 KiB
Rust
198 lines
6.1 KiB
Rust
use std::collections::HashMap;
|
|
use std::env;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use anyhow::Result;
|
|
use ignore::WalkBuilder;
|
|
|
|
const CYAN: &str = "\x1b[36m";
|
|
const GREEN: &str = "\x1b[32m";
|
|
const YELLOW: &str = "\x1b[33m";
|
|
const RESET: &str = "\x1b[0m";
|
|
const SEPARATOR: &str = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━";
|
|
|
|
pub fn run(path: Option<PathBuf>) -> Result<()> {
|
|
let path = path.unwrap_or_else(|| env::current_dir().unwrap());
|
|
let mut languages: HashMap<&str, (usize, usize)> = HashMap::new();
|
|
let mut livewire_php_lines: usize = 0;
|
|
let mut livewire_blade_lines: usize = 0;
|
|
let mut livewire_files: usize = 0;
|
|
let mut test_lines: usize = 0;
|
|
let mut test_files: usize = 0;
|
|
|
|
for entry in WalkBuilder::new(&path).build() {
|
|
let entry = entry?;
|
|
if !entry.file_type().map_or(false, |ft| ft.is_file()) {
|
|
continue;
|
|
}
|
|
|
|
let file_path = entry.path();
|
|
let filename = file_path.file_name().unwrap_or_default().to_string_lossy();
|
|
|
|
if is_lock_file(&filename) {
|
|
continue;
|
|
}
|
|
|
|
// Livewire SFC: ⚡*.blade.php (excluding test files)
|
|
if filename.starts_with('⚡')
|
|
&& filename.ends_with(".blade.php")
|
|
&& !filename.ends_with(".test.php")
|
|
{
|
|
let Ok(content) = fs::read_to_string(file_path) else {
|
|
continue;
|
|
};
|
|
livewire_files += 1;
|
|
let mut in_php = true;
|
|
for line in content.lines() {
|
|
if in_php && line.trim() == "?>" {
|
|
in_php = false;
|
|
} else if in_php {
|
|
livewire_php_lines += 1;
|
|
} else {
|
|
livewire_blade_lines += 1;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// PHP tests: *.test.php or .php in tests/ directory
|
|
let in_tests_dir = file_path.components().any(|c| c.as_os_str() == "tests");
|
|
if filename.ends_with(".test.php") || (in_tests_dir && filename.ends_with(".php")) {
|
|
let Ok(content) = fs::read_to_string(file_path) else {
|
|
continue;
|
|
};
|
|
test_lines += content.lines().count();
|
|
test_files += 1;
|
|
continue;
|
|
}
|
|
|
|
// Blade templates: *.blade.php (not Livewire SFC)
|
|
if filename.ends_with(".blade.php") {
|
|
let Ok(content) = fs::read_to_string(file_path) else {
|
|
continue;
|
|
};
|
|
let entry = languages.entry("Blade").or_insert((0, 0));
|
|
entry.0 += content.lines().count();
|
|
entry.1 += 1;
|
|
continue;
|
|
}
|
|
|
|
if let Some(lang) = detect_language(file_path) {
|
|
let Ok(content) = fs::read_to_string(file_path) else {
|
|
continue;
|
|
};
|
|
let entry = languages.entry(lang).or_insert((0, 0));
|
|
entry.0 += content.lines().count();
|
|
entry.1 += 1;
|
|
}
|
|
}
|
|
|
|
let mut sorted: Vec<_> = languages.into_iter().collect();
|
|
sorted.sort_by(|a, b| b.1 .0.cmp(&a.1 .0));
|
|
|
|
println!("{CYAN}📊 Statistiques du code{RESET}");
|
|
println!("{SEPARATOR}");
|
|
|
|
let mut total_lines: usize = 0;
|
|
let mut total_files: usize = 0;
|
|
|
|
if livewire_files > 0 {
|
|
print_line(
|
|
GREEN,
|
|
"Livewire SFC (PHP)",
|
|
livewire_php_lines,
|
|
Some(livewire_files),
|
|
);
|
|
print_line(GREEN, "Livewire SFC (Blade)", livewire_blade_lines, None);
|
|
total_lines += livewire_php_lines + livewire_blade_lines;
|
|
total_files += livewire_files;
|
|
}
|
|
|
|
for (lang, (lines, files)) in &sorted {
|
|
print_line(GREEN, lang, *lines, Some(*files));
|
|
total_lines += lines;
|
|
total_files += files;
|
|
}
|
|
|
|
if test_files > 0 {
|
|
print_line(YELLOW, "Tests", test_lines, Some(test_files));
|
|
}
|
|
|
|
println!("{SEPARATOR}");
|
|
print_line(CYAN, "Total (hors tests)", total_lines, Some(total_files));
|
|
print_line(
|
|
CYAN,
|
|
"Total",
|
|
total_lines + test_lines,
|
|
Some(total_files + test_files),
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn print_line(color: &str, name: &str, lines: usize, files: Option<usize>) {
|
|
match files {
|
|
Some(f) => println!(
|
|
"{color}{:<25}{RESET} {:>6} lignes ({f} fichiers)",
|
|
name, lines
|
|
),
|
|
None => println!("{color}{:<25}{RESET} {:>6} lignes", name, lines),
|
|
}
|
|
}
|
|
|
|
fn is_lock_file(filename: &str) -> bool {
|
|
matches!(
|
|
filename,
|
|
"composer.lock"
|
|
| "package-lock.json"
|
|
| "yarn.lock"
|
|
| "pnpm-lock.yaml"
|
|
| "Cargo.lock"
|
|
| "Gemfile.lock"
|
|
| "poetry.lock"
|
|
| "flake.lock"
|
|
)
|
|
}
|
|
|
|
fn detect_language(path: &Path) -> Option<&'static str> {
|
|
let filename = path.file_name()?.to_str()?;
|
|
match filename {
|
|
"Dockerfile" | "Containerfile" => return Some("Docker"),
|
|
"Makefile" | "GNUmakefile" => return Some("Makefile"),
|
|
_ => {}
|
|
}
|
|
|
|
let ext = path.extension()?.to_str()?;
|
|
match ext {
|
|
"php" => Some("PHP"),
|
|
"js" | "mjs" | "cjs" => Some("JavaScript"),
|
|
"ts" | "mts" | "cts" => Some("TypeScript"),
|
|
"jsx" => Some("JSX"),
|
|
"tsx" => Some("TSX"),
|
|
"rs" => Some("Rust"),
|
|
"py" | "pyw" => Some("Python"),
|
|
"rb" => Some("Ruby"),
|
|
"go" => Some("Go"),
|
|
"java" => Some("Java"),
|
|
"kt" | "kts" => Some("Kotlin"),
|
|
"swift" => Some("Swift"),
|
|
"c" | "h" => Some("C"),
|
|
"cpp" | "cc" | "cxx" | "hpp" | "hxx" | "hh" => Some("C++"),
|
|
"cs" => Some("C#"),
|
|
"hs" | "lhs" => Some("Haskell"),
|
|
"ex" | "exs" => Some("Elixir"),
|
|
"erl" | "hrl" => Some("Erlang"),
|
|
"dart" => Some("Dart"),
|
|
"lua" => Some("Lua"),
|
|
"r" | "R" => Some("R"),
|
|
"scala" | "sc" => Some("Scala"),
|
|
"zig" => Some("Zig"),
|
|
"sh" | "bash" | "zsh" | "fish" => Some("Shell"),
|
|
"sql" => Some("SQL"),
|
|
"vue" => Some("Vue"),
|
|
"svelte" => Some("Svelte"),
|
|
_ => None,
|
|
}
|
|
}
|