zcolorizer 0.2.2

Real-time log file colorizer (ccze/pygments port) — fully customizable regex→token rules, swappable themes, cyberpunk by default
Documentation
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="color-scheme" content="dark light">
  <meta name="description" content="zcolorizer engineering report — module map, dependency footprint and test status for the Rust real-time log colorizer (ccze/pygments port).">
  <title>zcolorizer &mdash; Engineering Report</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&family=Share+Tech+Mono&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="hud-static.css">
  <link rel="stylesheet" href="tutorial.css">
  <style>
    .tutorial-main { max-width: 76rem; }
    .bar-wrap { background:var(--bg-primary);border:1px solid var(--border);border-radius:2px;height:18px;position:relative;overflow:hidden; }
    .bar-fill { height:100%;border-radius:1px;transition:width 1.2s cubic-bezier(.22,1,.36,1); }
    .bar-fill.green  { background:linear-gradient(90deg,#39ff14,#20c00a);box-shadow:0 0 8px rgba(57,255,20,.4); }
    .bar-fill.cyan   { background:linear-gradient(90deg,#05d9e8,#0891b2);box-shadow:0 0 8px rgba(5,217,232,.4); }
    .bar-fill.yellow { background:linear-gradient(90deg,#ffb800,#e8a000);box-shadow:0 0 8px rgba(255,184,0,.35); }
    .bar-fill.magenta{ background:linear-gradient(90deg,#d300c5,#a000a0);box-shadow:0 0 8px rgba(211,0,197,.35); }
    .bar-pct { position:absolute;right:6px;top:0;line-height:18px;font-size:10px;font-weight:700;color:#fff;text-shadow:0 0 4px #000;font-family:'Orbitron',sans-serif; }

    .file-table { width:100%;border-collapse:collapse;margin:0.6rem 0;font-size:12px; }
    .file-table th { background:var(--bg-secondary);color:var(--cyan);font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;text-align:left;padding:7px 10px;border:1px solid var(--border); }
    .file-table td { padding:6px 10px;border:1px solid var(--border);color:var(--text-dim);vertical-align:middle; }
    .file-table tr:hover td { background:var(--bg-hover); }
    .file-table td:first-child { font-family:'Share Tech Mono',monospace;color:var(--accent-light);font-weight:600;white-space:nowrap; }
    .file-table .num { text-align:right;font-family:'Share Tech Mono',monospace; }
    .file-table .total-row td { background:var(--bg-secondary);font-weight:700;color:var(--text);border-top:2px solid var(--cyan); }
    .file-table code { font-size:11px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }

    .stat-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(14rem,1fr));gap:0.75rem;margin:1.2rem 0; }
    .stat-card { border:1px solid var(--border);border-top:3px solid var(--cyan);background:var(--bg-card);padding:1rem 1.2rem;border-radius:2px;text-align:center; }
    .stat-card .stat-val { font-family:'Orbitron',sans-serif;font-size:28px;font-weight:900;color:var(--cyan);line-height:1.1;text-shadow:0 0 20px var(--cyan-glow); }
    .stat-card .stat-val.accent { color:var(--accent);text-shadow:0 0 20px var(--accent-glow); }
    .stat-card .stat-val.green  { color:var(--green);text-shadow:0 0 20px rgba(57,255,20,.3); }
    .stat-card .stat-label { font-family:'Orbitron',sans-serif;font-size:9px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--text-muted);margin-top:0.5rem; }
    @keyframes glow-pulse { 0%,100%{text-shadow:0 0 20px var(--cyan-glow)}50%{text-shadow:0 0 40px var(--cyan-glow),0 0 80px var(--cyan-dim)} }
    .stat-card .stat-val { animation:glow-pulse 3s ease-in-out infinite; }

    .mapping-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(20rem,1fr));gap:0.65rem;margin:0.8rem 0; }
    .mapping-card { border:1px solid var(--border);border-left:3px solid var(--magenta);background:var(--bg-card);padding:0.6rem 0.9rem;border-radius:2px; }
    .mapping-card h4 { font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--magenta);margin:0 0 0.3rem; }
    .mapping-card p { margin:0;font-size:11px;color:var(--text-dim);line-height:1.5; }
    .mapping-card code { font-size:10.5px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }

    .section-rule { border:none;border-top:1px dashed var(--border);margin:2rem 0; }

    .feature-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(22rem,1fr));gap:0.65rem;margin:0.8rem 0; }
    .feature-card { border:1px solid var(--border);border-left:3px solid var(--cyan);background:var(--bg-card);padding:0.7rem 1rem;border-radius:2px; }
    .feature-card h4 { font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--cyan);margin:0 0 0.3rem; }
    .feature-card p { margin:0;font-size:11px;color:var(--text-dim);line-height:1.55; }
    .feature-card code { font-size:10.5px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }
    .feature-card ul { margin:0.3rem 0 0;padding-left:1.2rem;font-size:11px;color:var(--text-dim);line-height:1.6; }
    .feature-card li code { font-size:10px; }
  </style>
</head>
<body>
  <div class="app tutorial-app" id="reportApp">
    <div class="crt-scanline" id="crtH" aria-hidden="true"></div>
    <div class="crt-scanline-v" id="crtV" aria-hidden="true"></div>
    <header class="tutorial-header">
      <div class="tutorial-header-inner">
        <div>
          <h1 class="tutorial-brand">// ZCOLORIZER &mdash; ENGINEERING REPORT</h1>
          <nav class="tutorial-crumbs" aria-label="Breadcrumb">
            <span class="current">Engineering Report</span>
            <span class="sep">/</span>
            <a href="index.html">Home</a>
            <span class="sep">/</span>
            <a href="https://github.com/MenkeTechnologies/zcolorizer" target="_blank" rel="noopener noreferrer">GitHub</a>
            <span class="sep">/</span>
            <a href="https://crates.io/crates/zcolorizer" target="_blank" rel="noopener noreferrer">crates.io</a>
            <span class="sep">/</span>
            <a href="https://docs.rs/zcolorizer" target="_blank" rel="noopener noreferrer">docs.rs</a>
          </nav>
          <p style="margin:0.35rem 0 0;font-family:'Share Tech Mono',monospace;font-size:11px;color:var(--text-dim);letter-spacing:0.03em;opacity:0.75;">
            module map &middot; dependency footprint &middot; test status &middot; ccze + pygments regex&rarr;token port
          </p>
        </div>
        <div class="tutorial-toolbar">
          <button type="button" class="btn btn-secondary" id="btnTheme" title="Toggle light/dark">Theme</button>
          <button type="button" class="btn btn-secondary active" id="btnCrt" title="CRT scanline overlay">CRT</button>
          <button type="button" class="btn btn-secondary active" id="btnNeon" title="Neon border pulse">Neon</button>
        </div>
      </div>
    </header>
    <main class="tutorial-main">

      <h2 class="tutorial-title"><span class="step-hash">&gt;_</span>EXECUTIVE SUMMARY</h2>
      <p class="tutorial-subtitle">zcolorizer v0.2.2 is a single-crate Rust workspace (~3,855 lines across 10 source files) that ports <strong>ccze</strong> and the <strong>pygments</strong> regex&rarr;token idea. The pipeline is three decoupled pieces: <strong>rules</strong> tag spans of a line with semantic tokens, a <strong>theme</strong> maps each token to a style, and the <strong>engine</strong> paints the claimed spans. It ships a library (rlib + staticlib + cdylib) and a CLI binary, with the clap dependency confined to the CLI behind the default <code>cli</code> feature.</p>
      <div class="stat-grid">
        <div class="stat-card"><div class="stat-val accent">10</div><div class="stat-label">Rust source files</div></div>
        <div class="stat-card"><div class="stat-val">~3,855</div><div class="stat-label">Lines of Rust</div></div>
        <div class="stat-card"><div class="stat-val">47</div><div class="stat-label">Format modules</div></div>
        <div class="stat-card"><div class="stat-val">32</div><div class="stat-label">Built-in themes</div></div>
        <div class="stat-card"><div class="stat-val green">108</div><div class="stat-label">Tests</div></div>
        <div class="stat-card"><div class="stat-val">8</div><div class="stat-label">Direct dependencies</div></div>
      </div>

      <hr class="section-rule">

      <h2 class="tutorial-title"><span class="step-hash">~</span>MODULE MAP</h2>
      <p class="tutorial-subtitle">The crate is organized around the rules &rarr; theme &rarr; engine pipeline. The two module files (<code>modules.rs</code> + <code>modules_modern.rs</code>) carry the bulk of the format ports, while <code>theme.rs</code> holds the 32 builtin palettes. Bar widths are relative to the largest file.</p>
      <table class="file-table">
        <thead><tr><th>Source</th><th class="num">Lines</th><th>Size</th><th>Role</th></tr></thead>
        <tbody>
          <tr><td><code>src/lib.rs</code></td><td class="num">60</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:9%"></div></div></td><td>Crate root and public API; documents the rules &rarr; theme &rarr; engine pipeline.</td></tr>
          <tr><td><code>src/rules.rs</code></td><td class="num">257</td><td><div class="bar-wrap"><div class="bar-fill magenta" style="width:40%"></div></div></td><td><code>RuleDef</code>/<code>Rule</code>: regex&rarr;token rules — named-group rules (group name is the token) and whole-match rules.</td></tr>
          <tr><td><code>src/theme.rs</code></td><td class="num">517</td><td><div class="bar-wrap"><div class="bar-fill green" style="width:81%"></div></div></td><td>Token&rarr;<code>Style</code> themes; 31 cyberpunk palettes plus <code>ccze-classic</code> (32 builtins), <code>neon-sprawl</code> default.</td></tr>
          <tr><td><code>src/color.rs</code></td><td class="num">386</td><td><div class="bar-wrap"><div class="bar-fill yellow" style="width:61%"></div></div></td><td><code>Color</code> (16 named / 256-index / truecolor RGB) and <code>Style</code>, with ANSI SGR rendering.</td></tr>
          <tr><td><code>src/engine.rs</code></td><td class="num">405</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:64%"></div></div></td><td><code>Colorizer</code>: runs compiled rules over each line; first rule to claim a byte owns it, mirroring ccze's module-then-wordcolor flow.</td></tr>
          <tr><td><code>src/config.rs</code></td><td class="num">363</td><td><div class="bar-wrap"><div class="bar-fill magenta" style="width:57%"></div></div></td><td>TOML config — merges theme, rules and module selections; XDG config path resolution.</td></tr>
          <tr><td><code>src/modules.rs</code></td><td class="num">622</td><td><div class="bar-wrap"><div class="bar-fill green" style="width:98%"></div></div></td><td><code>Module</code> type plus 20 classic ccze plugin ports (syslog, httpd, squid, dpkg, postfix, exim, ftp, php, ulogd…).</td></tr>
          <tr><td><code>src/modules_modern.rs</code></td><td class="num">635</td><td><div class="bar-wrap"><div class="bar-fill yellow" style="width:100%"></div></div></td><td>27 modern format modules: journald, authlog, kernel, nginx, haproxy, JSON/logfmt, postgres/mysql/redis/mongodb, docker/klog, fail2ban/ufw…</td></tr>
          <tr><td><code>src/error.rs</code></td><td class="num">32</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:5%"></div></div></td><td>Crate <code>Error</code> enum (thiserror): bad-rule regex, unknown theme, config/IO errors.</td></tr>
          <tr><td><code>src/bin/zcolorizer.rs</code></td><td class="num">578</td><td><div class="bar-wrap"><div class="bar-fill magenta" style="width:91%"></div></div></td><td>CLI binary (clap): streaming stdin/file colorization, theme/module/rule listing, config dump.</td></tr>
          <tr class="total-row"><td>10 files</td><td class="num">3,855</td><td></td><td>Library (rlib + staticlib + cdylib) plus the <code>cli</code>-feature binary.</td></tr>
        </tbody>
      </table>

      <hr class="section-rule">

      <h2 class="tutorial-title"><span class="step-hash">~</span>ARCHITECTURE</h2>
      <p class="tutorial-subtitle">Three independently editable pieces — change one without disturbing the others. Because color lives entirely in the theme, switching themes recolors every log without touching a single rule.</p>
      <div class="feature-grid">
        <div class="feature-card"><h4>Rules</h4><p>A rule is a regex whose <strong>named capture groups are tokens</strong> — one pattern can paint several fields (<code>date</code>, <code>host</code>, <code>error</code>). Whole-match rules use an explicit <code>token</code>. Fully editable in the TOML config.</p></div>
        <div class="feature-card"><h4>Themes</h4><p>A token&rarr;<code>Style</code> map you swap live. 31 cyberpunk palettes plus <code>ccze-classic</code> (32 builtins), default <code>neon-sprawl</code>. Colors are truecolor RGB, 256-index, or named ANSI.</p></div>
        <div class="feature-card"><h4>Engine</h4><p><code>Colorizer</code> runs the compiled rules over each line; the first rule to claim a byte owns it, mirroring ccze's module-then-wordcolor flow. Streams line-buffered so <code>tail -f</code> / <code>journalctl -f</code> stay live.</p></div>
      </div>

      <hr class="section-rule">

      <h2 class="tutorial-title"><span class="step-hash">~</span>DEPENDENCY FOOTPRINT</h2>
      <p class="tutorial-subtitle">Eight direct dependencies, all foundational and durable; clap is optional and CLI-only. All are dual-licensed MIT OR Apache-2.0, matching zcolorizer's own MIT license. The vendored ccze C source is a build-time reference only and is excluded from the published crate — the Rust port is original.</p>
      <table class="file-table">
        <thead><tr><th>Crate</th><th>Ver</th><th>License</th><th>Why</th></tr></thead>
        <tbody>
          <tr><td>serde</td><td>1</td><td>MIT / Apache-2.0</td><td>Derive (de)serialization for config, themes, rules.</td></tr>
          <tr><td>serde_json</td><td>1</td><td>MIT / Apache-2.0</td><td><code>--themes-json</code> emit for tooling.</td></tr>
          <tr><td>toml</td><td>0.8</td><td>MIT / Apache-2.0</td><td>Parse/emit the on-disk config and <code>--dump-config</code>.</td></tr>
          <tr><td>regex</td><td>1</td><td>MIT / Apache-2.0</td><td>Compiles the rule patterns that tag tokens.</td></tr>
          <tr><td>thiserror</td><td>2</td><td>MIT / Apache-2.0</td><td>Derives the crate error type.</td></tr>
          <tr><td>dirs</td><td>6</td><td>MIT / Apache-2.0</td><td>Resolves the XDG config directory.</td></tr>
          <tr><td>once_cell</td><td>1</td><td>MIT / Apache-2.0</td><td>Lazily-initialized builtin tables.</td></tr>
          <tr><td>clap (optional)</td><td>4</td><td>MIT / Apache-2.0</td><td>CLI argument parsing — only under the <code>cli</code> feature; the library never depends on it.</td></tr>
        </tbody>
      </table>

      <hr class="section-rule">

      <h2 class="tutorial-title"><span class="step-hash">~</span>VERIFICATION</h2>
      <p class="tutorial-subtitle">108 tests in total: 91 unit tests embedded across the source modules plus 17 integration tests in <code>tests/integration.rs</code>. The integration suite drives the real colorizer through tagging assertions — verifying that syslog/httpd/squid/dpkg/postfix/exim/php/vsftpd modules paint the correct fields, that swapping a theme changes color without changing structure, that the generic word-colorizer assigns distinct tokens to good/bad word lists, that replace-mode uses only user rules, that multi-line input colorizes each line, and that config round-trips through TOML. Release profile builds with LTO and symbol stripping. Run with <code>cargo test</code>.</p>
      <table class="file-table">
        <thead><tr><th>Suite</th><th class="num">Tests</th><th>Scope</th></tr></thead>
        <tbody>
          <tr><td>engine</td><td class="num">21</td><td>Rule application, span claiming, line painting.</td></tr>
          <tr><td>config</td><td class="num">19</td><td>TOML merge, theme/rule/module selection, round-trip.</td></tr>
          <tr><td>color</td><td class="num">14</td><td>Named / 256-index / truecolor parsing and ANSI SGR.</td></tr>
          <tr><td>theme</td><td class="num">14</td><td>Builtin palettes, token&rarr;style lookup, defaults.</td></tr>
          <tr><td>modules</td><td class="num">11</td><td>Classic ccze plugin ports.</td></tr>
          <tr><td>rules</td><td class="num">10</td><td>Named-group and whole-match rule definitions.</td></tr>
          <tr><td>modules_modern</td><td class="num">2</td><td>Modern format module ports.</td></tr>
          <tr><td>tests/integration.rs</td><td class="num">17</td><td>End-to-end tagging assertions across modules + themes.</td></tr>
          <tr class="total-row"><td>Total</td><td class="num">108</td><td>91 unit + 17 integration.</td></tr>
        </tbody>
      </table>

      <hr class="section-rule">

      <h2 class="tutorial-title"><span class="step-hash">~</span>PROVENANCE</h2>
      <p class="tutorial-subtitle">The Rust port is original; no GPL code ships in the published crate.</p>
      <div class="feature-grid">
        <div class="feature-card"><h4>Upstream</h4><p>Ported from <a href="https://github.com/cornet/ccze">ccze</a> and the pygments regex&rarr;token idea. The vendored ccze C source is a build-time reference only and is excluded from the published crate.</p></div>
        <div class="feature-card"><h4>Distribution</h4><p>Ships as a library (rlib + staticlib + cdylib) and a CLI binary. Available on <a href="https://crates.io/crates/zcolorizer">crates.io</a> (<code>cargo install zcolorizer</code>) with API docs on <a href="https://docs.rs/zcolorizer">docs.rs</a>.</p></div>
        <div class="feature-card"><h4>License</h4><p>MIT. All eight direct dependencies are dual-licensed MIT OR Apache-2.0, matching the crate's own license.</p></div>
      </div>

    </main>
    <footer style="text-align:center;padding:2rem;font-size:10px;color:var(--text-muted);font-family:'Orbitron',sans-serif;letter-spacing:2px;">
      ZCOLORIZER &middot; ENGINEERING REPORT &middot; 10 RUST FILES / ~3,855 LINES &middot; 47 FORMAT MODULES &middot; 32 THEMES &middot; 108 TESTS &middot; MENKETECHNOLOGIES
    </footer>
  </div>

  <script>
    const html = document.documentElement;
    const btnTheme = document.getElementById('btnTheme');
    const btnCrt = document.getElementById('btnCrt');
    const btnNeon = document.getElementById('btnNeon');
    const crtH = document.getElementById('crtH');
    const crtV = document.getElementById('crtV');
    btnTheme?.addEventListener('click', () => {
      html.setAttribute('data-theme', html.getAttribute('data-theme') === 'light' ? 'dark' : 'light');
    });
    btnCrt?.addEventListener('click', () => {
      btnCrt.classList.toggle('active');
      const on = btnCrt.classList.contains('active');
      if (crtH) crtH.style.display = on ? '' : 'none';
      if (crtV) crtV.style.display = on ? '' : 'none';
    });
    btnNeon?.addEventListener('click', () => {
      btnNeon.classList.toggle('active');
      document.querySelector('.app')?.classList.toggle('neon-off');
    });
    document.addEventListener('DOMContentLoaded', () => {
      document.querySelectorAll('.bar-fill').forEach(bar => {
        const w = bar.style.width;
        bar.style.width = '0';
        requestAnimationFrame(() => { requestAnimationFrame(() => { bar.style.width = w; }); });
      });
    });
  </script>
  <script src="hud-theme.js"></script>
</body>
</html>