tokmd_analysis_types/
util.rs1use std::path::{Path, PathBuf};
2#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
3use std::time::{SystemTime, UNIX_EPOCH};
4
5use crate::FileStatRow;
6
7#[derive(Debug, Clone, Default)]
8pub struct AnalysisLimits {
9 pub max_files: Option<usize>,
10 pub max_bytes: Option<u64>,
11 pub max_file_bytes: Option<u64>,
12 pub max_commits: Option<usize>,
13 pub max_commit_files: Option<usize>,
14}
15
16#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
17pub fn now_ms() -> u128 {
18 js_sys::Date::now().max(1.0) as u128
20}
21
22#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
23pub fn now_ms() -> u128 {
24 SystemTime::now()
25 .duration_since(UNIX_EPOCH)
26 .unwrap_or_default()
27 .as_millis()
28}
29
30pub fn normalize_path(path: &str, root: &Path) -> String {
31 let mut out = path.replace('\\', "/");
32 if let Ok(stripped) = Path::new(&out).strip_prefix(root) {
33 out = stripped.to_string_lossy().replace('\\', "/");
34 }
35 while let Some(stripped) = out.strip_prefix("./") {
36 out = stripped.to_string();
37 }
38 out
39}
40
41pub fn path_depth(path: &str) -> usize {
42 path.split('/').filter(|seg| !seg.is_empty()).count().max(1)
43}
44
45pub fn is_test_path(path: &str) -> bool {
46 let lower = path.to_lowercase();
47 if lower.contains("/test/") || lower.contains("/tests/") || lower.contains("__tests__") {
48 return true;
49 }
50 if lower.contains("/spec/") || lower.contains("/specs/") {
51 return true;
52 }
53 let name = lower.rsplit('/').next().unwrap_or(&lower);
54 name.contains("_test")
55 || name.contains(".test.")
56 || name.contains(".spec.")
57 || name.starts_with("test_")
58 || name.ends_with("_test.rs")
59}
60
61pub fn is_infra_lang(lang: &str) -> bool {
62 let l = lang.to_lowercase();
63 matches!(
64 l.as_str(),
65 "json"
66 | "yaml"
67 | "toml"
68 | "markdown"
69 | "xml"
70 | "html"
71 | "css"
72 | "scss"
73 | "less"
74 | "makefile"
75 | "dockerfile"
76 | "hcl"
77 | "terraform"
78 | "nix"
79 | "cmake"
80 | "ini"
81 | "properties"
82 | "gitignore"
83 | "gitconfig"
84 | "editorconfig"
85 | "csv"
86 | "tsv"
87 | "svg"
88 )
89}
90
91pub fn empty_file_row() -> FileStatRow {
92 FileStatRow {
93 path: String::new(),
94 module: String::new(),
95 lang: String::new(),
96 code: 0,
97 comments: 0,
98 blanks: 0,
99 lines: 0,
100 bytes: 0,
101 tokens: 0,
102 doc_pct: None,
103 bytes_per_line: None,
104 depth: 0,
105 }
106}
107
108pub fn normalize_root(root: &Path) -> PathBuf {
109 root.canonicalize().unwrap_or_else(|_| root.to_path_buf())
110}
111
112#[cfg(test)]
113mod tests;