1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2026 Nima Shafie <nimzshafie@gmail.com>
//! # oxide-sloc
//!
//! **Cross-platform source line analysis tool** implementing IEEE 1045-1992 SLOC
//! metrics, unit-test detection, coverage reporting, and CI/CD enforcement — with
//! a localhost web UI, rich HTML/PDF reports, and deep Git integration.
//!
//! ## Overview
//!
//! `oxide-sloc` is a binary crate — its primary interface is the `oxide-sloc`
//! executable. All analytical logic lives in focused library crates published
//! separately on crates.io for programmatic use:
//!
//! | Crate | Purpose |
//! |---|---|
//! | [`sloc-config`](https://docs.rs/sloc-config) | Configuration schema and TOML parsing |
//! | [`sloc-languages`](https://docs.rs/sloc-languages) | Language detection and lexical line analyzers |
//! | [`sloc-core`](https://docs.rs/sloc-core) | File discovery, decoding, and aggregation engine |
//! | [`sloc-report`](https://docs.rs/sloc-report) | HTML rendering and PDF export |
//! | [`sloc-git`](https://docs.rs/sloc-git) | Git CLI wrappers and webhook parsing |
//! | [`sloc-web`](https://docs.rs/sloc-web) | Axum web server and form handlers |
//!
//! ## Installation
//!
//! ```text
//! cargo install oxide-sloc
//! ```
//!
//! Or clone the repository and run `bash scripts/run.sh` for a fully offline,
//! no-Rust-required build (all Rust crates and the toolchain are vendored in the
//! repository — no internet connection or pre-installed Rust required).
//!
//! ## Quick Start
//!
//! ```text
//! # Start the localhost web UI → http://127.0.0.1:4317
//! oxide-sloc
//!
//! # Scan a directory, print a summary
//! oxide-sloc analyze ./my-repo --plain
//!
//! # Produce JSON + HTML + PDF reports in one pass
//! oxide-sloc analyze ./my-repo \
//! --json-out result.json \
//! --html-out result.html \
//! --pdf-out result.pdf
//!
//! # Re-render a saved JSON result without re-scanning
//! oxide-sloc report result.json --html-out result.html --pdf-out result.pdf
//!
//! # Compare two saved scans and show the delta
//! oxide-sloc diff baseline.json current.json
//!
//! # Generate a starter config file
//! oxide-sloc init
//! ```
//!
//! ## Subcommands
//!
//! | Subcommand | Description |
//! |---|---|
//! | *(none)* | Alias for `serve` — starts the web UI |
//! | `analyze` | Scan one or more directories and produce metrics |
//! | `report` | Re-render a saved JSON result as HTML / PDF / CSV / XLSX |
//! | `diff` | Compare two JSON results and show the delta |
//! | `serve` | Start the Axum web UI (port 4317 by default) |
//! | `init` | Write a starter `.oxide-sloc.toml` config file |
//! | `validate` | Validate config file paths and glob patterns |
//! | `send` | Deliver a report via SMTP, webhook, Microsoft Teams, or Confluence |
//! | `git-scan` | Clone a repo and scan it at a specific branch / tag / SHA |
//! | `git-compare` | Scan two git refs and produce a diff report |
//! | `watch` | Poll a branch and scan on every new commit |
//! | `pr-comment` | Post an SLOC diff as a GitHub / GitLab PR comment |
//! | `completions` | Print a shell completion script (bash / zsh / fish / …) |
//!
//! ## Output formats
//!
//! All formats are produced by the `analyze` subcommand via output flags.
//! The `report` subcommand can re-render any of them from a saved JSON result.
//!
//! | Flag | Format | Notes |
//! |---|---|---|
//! | `--json-out` | JSON | Full `AnalysisRun` struct; machine-readable and round-trippable |
//! | `--html-out` | HTML | Self-contained, inline CSS, light/dark theme, charts |
//! | `--pdf-out` | PDF | Rendered via headless Chromium (Chrome/Edge/Brave/Vivaldi/Opera) |
//! | `--csv-out` | CSV | Per-language summary; suitable for spreadsheets |
//! | `--xlsx-out` | Excel | Full workbook with per-language and per-file sheets |
//! | `--plain` | key=value | Machine-readable terminal output for shell scripting |
//!
//! ## CI/CD Integration
//!
//! ### GitHub Actions / Jenkins — basic scan
//!
//! ```text
//! oxide-sloc analyze . \
//! --json-out sloc.json \
//! --html-out sloc.html \
//! --fail-on-warnings \
//! --fail-below 1000
//! ```
//!
//! ### SLOC budget enforcement
//!
//! Define per-language ceilings in `.oxide-sloc.toml` and add `--fail-on-budget`
//! to your CI step. Exit code 4 when any threshold is exceeded.
//!
//! ```toml
//! [analysis.budget]
//! total_max = 200000 # hard ceiling across all languages
//! rust = 120000
//! typescript = 60000
//! ```
//!
//! ### Baseline tracking
//!
//! ```text
//! # Save the current scan as a named snapshot
//! oxide-sloc analyze . --json-out sloc.json --set-baseline main
//!
//! # On the next PR — fail if code grew more than 5 %
//! oxide-sloc analyze . --json-out sloc.json \
//! --fail-above-baseline main --max-delta-pct 5
//! ```
//!
//! ### PR diff comment (GitHub / GitLab)
//!
//! Posts an Adaptive Card–style comment to the pull request with code-line deltas.
//!
//! ```text
//! oxide-sloc pr-comment current.json \
//! --baseline baseline.json \
//! --provider github \
//! --repo owner/repo \
//! --pr-number 42 \
//! --token "$GITHUB_TOKEN" \
//! --report-url "https://ci.example.com/sloc.html"
//! ```
//!
//! ### LCOV coverage overlay
//!
//! Attach per-file coverage data produced by `cargo-llvm-cov`, `gcov`, or any
//! LCOV-compatible tool:
//!
//! ```text
//! oxide-sloc analyze . --coverage-file lcov.info --html-out sloc.html
//! ```
//!
//! ### Exit codes
//!
//! | Code | Meaning |
//! |---|---|
//! | 0 | Success |
//! | 1 | Analysis or I/O error |
//! | 2 | `--fail-on-warnings`: one or more warnings emitted |
//! | 3 | `--fail-below N`: code lines fell below the threshold |
//! | 4 | `--fail-on-budget`: a budget ceiling was exceeded |
//! | 5 | `--fail-above-baseline`: code grew beyond the allowed delta |
//!
//! ## Configuration
//!
//! Generate a starter config with `oxide-sloc init`, then edit as needed.
//! The config file is loaded automatically when `.oxide-sloc.toml` exists in the
//! current directory; pass `--config <path>` to override.
//!
//! ```toml
//! [discovery]
//! root_paths = ["."]
//! exclude_globs = ["**/node_modules/**", "**/target/**"]
//! honor_ignore_files = true # respect .gitignore / .ignore
//! follow_symlinks = false
//! submodule_breakdown = false # per-submodule stats in output
//!
//! [analysis]
//! enabled_languages = [] # empty = all 60 languages
//! mixed_line_policy = "code-only"
//! python_docstrings_as_comments = true
//! generated_file_detection = true
//! vendor_directory_detection = true
//!
//! # IEEE 1045-1992 counting parameters
//! continuation_line_policy = "each-physical-line"
//! blank_in_block_comment_policy = "count-as-comment"
//! count_compiler_directives = true
//!
//! [reporting]
//! report_title = "SLOC Report"
//! theme = "auto" # auto | light | dark
//! company_name = "Acme Corp" # optional branding
//! accent_color = "#3b82f6" # optional hex colour
//!
//! [web]
//! bind_address = "127.0.0.1:4317"
//! server_mode = false # true = bind 0.0.0.0, LAN mode
//! ```
//!
//! ### Named profiles
//!
//! Override any config section per invocation with `--profile <name>`:
//!
//! ```toml
//! [profile.frontend]
//! [profile.frontend.discovery]
//! root_paths = ["frontend"]
//! exclude_globs = ["**/node_modules/**", "**/dist/**"]
//! [profile.frontend.analysis]
//! enabled_languages = ["TypeScript", "JavaScript", "CSS"]
//! ```
//!
//! ## Web UI
//!
//! Running `oxide-sloc` with no arguments (or `oxide-sloc serve`) starts the
//! Axum web UI at `http://127.0.0.1:4317`.
//!
//! - **Step 1** — pick a directory with the native file picker, choose output
//! formats, configure discovery and analysis options.
//! - **Step 2** — review the live analysis results, download JSON/HTML/PDF/CSV/XLSX.
//! - **View Reports** — browse all past runs with trend charts.
//! - **Compare Scans** — side-by-side delta between any two saved runs.
//! - **Git Tools** — webhook configuration and repository integration.
//!
//! For LAN / multi-user access:
//!
//! ```text
//! oxide-sloc serve --server # binds 0.0.0.0:4317
//! oxide-sloc serve --bind 0.0.0.0:8080
//! ```
//!
//! ## Delivery (`send` subcommand)
//!
//! Ship a saved analysis result to one or more destinations in a single call:
//!
//! ```text
//! oxide-sloc send result.json \
//! --smtp-to team@example.com \
//! --smtp-from ci@example.com \
//! --smtp-host mail.example.com \
//! --notify-teams "$TEAMS_WEBHOOK_URL" \
//! --webhook-url "$DASHBOARD_URL"
//! ```
//!
//! | Destination | Flag(s) |
//! |---|---|
//! | Email (SMTP/TLS) | `--smtp-to`, `--smtp-from`, `--smtp-host` |
//! | HTTP webhook | `--webhook-url`, `--webhook-token` |
//! | Microsoft Teams | `--notify-teams` |
//! | Confluence | `--confluence-url`, `--confluence-space`, `--confluence-page-title` |
//!
//! ## Git operations
//!
//! Scan a remote repository at a specific ref without a manual clone:
//!
//! ```text
//! # Scan the v2.0.0 tag of a remote repo
//! oxide-sloc git-scan https://github.com/org/repo --git-ref v2.0.0 \
//! --json-out v2.json --html-out v2.html
//!
//! # Compare two tags
//! oxide-sloc git-compare https://github.com/org/repo v1.0.0 v2.0.0 \
//! --json-out delta.json
//!
//! # Watch a branch and scan on every new commit (poll every 5 min)
//! oxide-sloc watch https://github.com/org/repo \
//! --branch main --interval 300 --output-dir scans/
//! ```
//!
//! ## Environment variables
//!
//! | Variable | Purpose |
//! |---|---|
//! | `SLOC_BIND` | Override web bind address (lower priority than `--bind`) |
//! | `SLOC_API_KEY` | Enable bearer-token auth on the web server |
//! | `SLOC_ALLOWED_ROOTS` | Colon-separated list of directories the web UI may scan |
//! | `SLOC_TLS_CERT` / `SLOC_TLS_KEY` | Enable native TLS on the web server |
//! | `SLOC_BROWSER` | Override the browser binary used for PDF export |
//! | `SLOC_BROWSER_NOSANDBOX` | Set to `1` to pass `--no-sandbox` (required in Docker) |
//! | `SLOC_COVERAGE_FILE` | Path to an LCOV `.info` file (same as `--coverage-file`) |
//! | `SLOC_SMTP_HOST` / `SLOC_SMTP_USER` / `SLOC_SMTP_PASS` | SMTP credentials |
//! | `SLOC_WEBHOOK_TOKEN` | Bearer token for webhook delivery |
//! | `SLOC_ALLOW_PRIVATE_WEBHOOK` | Set to `1` to allow HTTP and private-IP webhooks |
//! | `SLOC_VCS_API_URL` / `SLOC_VCS_REPO` / `SLOC_VCS_TOKEN` | VCS API for `pr-comment` |
//! | `SLOC_PR_NUMBER` | PR number for `pr-comment` |
//! | `SLOC_CONFLUENCE_URL` / `SLOC_CONFLUENCE_USER` / `SLOC_CONFLUENCE_TOKEN` | Confluence credentials |
//! | `SLOC_CONFLUENCE_SPACE` | Confluence space key |
//! | `RUST_LOG` | Tracing log level (e.g. `RUST_LOG=debug`) |
//!
//! ## Supported languages
//!
//! 60 languages detected by file extension and shebang:
//!
//! Ada, Assembly, Awk, C, C++, C#, Clojure, `CMake`, Crystal, CSS, D, Dart,
//! Dockerfile, Elixir, Elm, Erlang, F#, Fortran, GLSL/HLSL, Go, GraphQL, Groovy,
//! Haskell, HCL/Terraform, HTML, Java, JavaScript, Julia, Kotlin, Lisp/Scheme,
//! Lua, Makefile, Nim, Nix, Objective-C, OCaml, Pascal/Delphi, Perl, PHP,
//! PowerShell, Protocol Buffers, Python, R, Ruby, Rust, Scala, SCSS/Sass,
//! Shell (bash/sh/zsh/ksh), Solidity, SQL, Svelte, Swift, Tcl, TypeScript,
//! Verilog/SystemVerilog, VHDL, Visual Basic, Vue, XML/SVG, and Zig.
//!
//! TOML, Markdown, and YAML are intentionally excluded — no meaningful SLOC
//! metric applies to them.
//!
//! Extension-to-language mappings can be overridden per project:
//!
//! ```toml
//! [analysis.extension_overrides]
//! "h" = "cpp" # treat .h files as C++ instead of C
//! ```
//!
//! ## IEEE 1045-1992 compliance
//!
//! The counting engine implements the IEEE 1045-1992 standard. Three parameters
//! let you choose the counting convention that matches your organisation's policy:
//!
//! | Parameter | Values | Default |
//! |---|---|---|
//! | `continuation_line_policy` | `each-physical-line` / `collapse-to-logical` | `each-physical-line` |
//! | `blank_in_block_comment_policy` | `count-as-comment` / `count-as-blank` | `count-as-comment` |
//! | `count_compiler_directives` | `true` / `false` | `true` |
//!
//! CLI flags (`--continuation-line-policy`, `--blank-in-block-comment-policy`,
//! `--no-count-compiler-directives`) override the config file for a single run.
//!
//! ## See also
//!
//! - [Repository & full documentation](https://github.com/oxide-sloc/oxide-sloc)
//! - [Changelog](https://github.com/oxide-sloc/oxide-sloc/releases)
//! - [`sloc-core`](https://docs.rs/sloc-core) — programmatic analysis API
//! - [`sloc-config`](https://docs.rs/sloc-config) — configuration schema
//! - [`sloc-languages`](https://docs.rs/sloc-languages) — per-language analyzers