From d613d51d4198628f6a821e1e9f6c419f5a2f968c Mon Sep 17 00:00:00 2001 From: yukkop Date: Fri, 31 Jan 2025 23:33:06 +0000 Subject: [PATCH] feat: pretify_log utilit --- .gitignore | 2 + flake.lock | 23 +- flake.nix | 52 ++- package/prettify-log/Cargo.lock | 434 ++++++++++++++++++ package/prettify-log/Cargo.toml | 18 + package/prettify-log/src/main.rs | 42 ++ .../prettify-log/test/fixture/expected.log | 87 ++++ package/prettify-log/test/fixture/test.log | 20 + package/prettify-log/test/prettify.rs | 36 ++ 9 files changed, 711 insertions(+), 3 deletions(-) create mode 100644 package/prettify-log/Cargo.lock create mode 100644 package/prettify-log/Cargo.toml create mode 100644 package/prettify-log/src/main.rs create mode 100644 package/prettify-log/test/fixture/expected.log create mode 100644 package/prettify-log/test/fixture/test.log create mode 100644 package/prettify-log/test/prettify.rs diff --git a/.gitignore b/.gitignore index 82af664..e571d24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .env result +rust-toolchain.toml +target/ diff --git a/flake.lock b/flake.lock index 2a5fc97..19dc284 100644 --- a/flake.lock +++ b/flake.lock @@ -17,7 +17,28 @@ }, "root": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1738290352, + "narHash": "sha256-YKOHUmc0Clm4tMV8grnxYL4IIwtjTayoq/3nqk0QM7k=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "b031b584125d33d23a0182f91ddbaf3ab4880236", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index e4b8448..fb4557b 100644 --- a/flake.nix +++ b/flake.nix @@ -2,9 +2,15 @@ description = "yukkop's nix utilities"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; }; - outputs = { self, nixpkgs }: + outputs = { self, nixpkgs, rust-overlay }: let lib = nixpkgs.lib; recursiveUpdate = lib.recursiveUpdate; @@ -42,7 +48,7 @@ else {}; in - forAllSystemsWithPkgs [] ({ system, pkgs }: + forAllSystemsWithPkgs [ (import rust-overlay) ] ({ system, pkgs }: { packages.${system} = { nvim-alias = pkgs.callPackage ./package/nvim-alias.nix {}; @@ -52,6 +58,48 @@ gh_translabeles = pkgs.callPackage ./package/github/gh_translabeles.nix {}; }; + devShells.${system} = + let + shells = self.devShells.${system}; + in + { + default = pkgs.mkShell { + buildInputs = (with self.packages.${system}; [ + nvim-alias + ]) ++ (with pkgs; [ + jq + yq-go + curl + ]); + + # environment + PAGER=''nvim -R -c 'set buftype=nofile' -c 'nnoremap q :q!' -c 'set nowrap' \ + -c 'set runtimepath^=${pkgs.vimPlugins.vim-plugin-AnsiEsc}' -c 'runtime! plugin/*.vim' -c 'AnsiEsc' -''; + # ^^^^^^^^^^^^^^^^^^^^ + # Prevents Neovim from treating the buffer as a file + # ^^^^^^^^^^^^^^^^^^^^ + # Makes 'q' quit Neovim immediately + # ^^^^^^^^^^^ + # Disables text wrapping + # ^^^^^^^^ + # Enables ANSI color interpretation + }; + rust = + let + rustToolchain = if builtins.pathExists ./rust-toolchain.toml then + pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml + else + pkgs.pkgsBuildHost.rust-bin.stable."1.81.0".default; + in + shells.default // { + nativeBuildInputs = [ + rustToolchain + pkgs.pkg-config + ]; + }; + }; + + nixosModules.${system} = { "hetzner.hardware" = { boot.loader.grub.device = "/dev/sda"; diff --git a/package/prettify-log/Cargo.lock b/package/prettify-log/Cargo.lock new file mode 100644 index 0000000..02326b7 --- /dev/null +++ b/package/prettify-log/Cargo.lock @@ -0,0 +1,434 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "assert_cmd" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "bstr" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettify-log" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "float-cmp", + "predicates", + "serde", + "serde_json", + "tempfile", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] diff --git a/package/prettify-log/Cargo.toml b/package/prettify-log/Cargo.toml new file mode 100644 index 0000000..8466c10 --- /dev/null +++ b/package/prettify-log/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "prettify-log" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = "1.0.217" +serde_json = "1.0.138" + +[dev-dependencies] +assert_cmd = "2.0.16" +predicates = "3.1.3" +float-cmp = "0.10.0" +tempfile = "3.16.0" + +[[test]] +name = "test_prettify" +path = "test/prettify.rs" diff --git a/package/prettify-log/src/main.rs b/package/prettify-log/src/main.rs new file mode 100644 index 0000000..635910e --- /dev/null +++ b/package/prettify-log/src/main.rs @@ -0,0 +1,42 @@ +use std::io::{self, BufRead}; +use serde_json::Value; + +/// Finds the substring from the first '{' to its matching '}', handling nested braces. +fn find_json_block(line: &str) -> Option<(usize, usize)> { + let start = line.find('{')?; + let mut brace_count = 0; + for (i, ch) in line[start..].char_indices() { + if ch == '{' { + brace_count += 1; + } else if ch == '}' { + brace_count -= 1; + if brace_count == 0 { + // Return the byte-range of the entire JSON block + return Some((start, start + i + 1)); + } + } + } + None +} + +fn main() -> io::Result<()> { + let stdin = io::stdin(); + + for line_result in stdin.lock().lines() { + let line = line_result?; + if let Some((start, end)) = find_json_block(&line) { + let candidate = &line[start..end]; + if let Ok(json) = serde_json::from_str::(candidate) { + let pretty = serde_json::to_string_pretty(&json).unwrap(); + let prefix = &line[..start]; + let suffix = &line[end..]; + println!("{}{}{}", prefix, pretty, suffix); + continue; + } + } + // If no valid JSON found, print as-is + println!("{}", line); + } + + Ok(()) +} diff --git a/package/prettify-log/test/fixture/expected.log b/package/prettify-log/test/fixture/expected.log new file mode 100644 index 0000000..12f73e4 --- /dev/null +++ b/package/prettify-log/test/fixture/expected.log @@ -0,0 +1,87 @@ +Debug: respons { + "some": "name", + "value": 12 +} is received +{timespan} Info: some log without json +Error: { + "code": 500, + "error": "Something went wrong" +} +Warning: Invalid data format detected +{ + "data": { + "id": 1, + "name": "test" + }, + "status": "ok" +} +User log: { + "action": "login", + "timestamp": "2025-01-31T12:00:00Z", + "user": "john_doe" +} +Random text without json +Debug: Payload sent: { + "request": { + "body": { + "key": "value" + }, + "type": "POST" + } +} +Another line with no json +{meta} Log: {"action": "update", "success": true} +Normal message with { + "json": "inside" +} text +{ + "array": [ + 1, + 2, + 3 + ], + "nested": { + "deep": { + "value": "found" + } + } +} +{2025-01-31} Event: {"event": "created", "user": "admin"} +Plain text that should not be modified +Info: { + "details": "this is a test log", + "level": "info" +} +{ + "array": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + } + ] +} +An unformatted json log { + "name": "example", + "valid": true +} +Debug: { + "data": "test", + "items": [ + { + "x": 10, + "y": 20 + } + ] +} +Non-json message with curly braces {like this} +{ + "key1": "value1", + "key2": { + "subkey": "subvalue" + } +} diff --git a/package/prettify-log/test/fixture/test.log b/package/prettify-log/test/fixture/test.log new file mode 100644 index 0000000..e30fd98 --- /dev/null +++ b/package/prettify-log/test/fixture/test.log @@ -0,0 +1,20 @@ +Debug: respons {"some": "name", "value": 12} is received +{timespan} Info: some log without json +Error: {"error": "Something went wrong", "code": 500} +Warning: Invalid data format detected +{"status": "ok", "data": {"id": 1, "name": "test"}} +User log: {"user": "john_doe", "action": "login", "timestamp": "2025-01-31T12:00:00Z"} +Random text without json +Debug: Payload sent: {"request": {"type": "POST", "body": {"key": "value"}}} +Another line with no json +{meta} Log: {"action": "update", "success": true} +Normal message with {"json": "inside"} text +{"nested": {"deep": {"value": "found"}}, "array": [1,2,3]} +{2025-01-31} Event: {"event": "created", "user": "admin"} +Plain text that should not be modified +Info: {"level": "info", "details": "this is a test log"} +{"array": [{"id": 1}, {"id": 2}, {"id": 3}]} +An unformatted json log {"name":"example","valid":true} +Debug: {"data": "test", "items": [{"x": 10, "y": 20}]} +Non-json message with curly braces {like this} +{"key1": "value1", "key2": {"subkey": "subvalue"}} diff --git a/package/prettify-log/test/prettify.rs b/package/prettify-log/test/prettify.rs new file mode 100644 index 0000000..505c1a6 --- /dev/null +++ b/package/prettify-log/test/prettify.rs @@ -0,0 +1,36 @@ +use std::fs; +use std::process::Command; +use std::io::Write; +use tempfile::NamedTempFile; + +#[test] +fn test_prettify() { + let input_file = "test/fixture/test.log"; + let expected_output = fs::read_to_string("test/fixture/expected.log") + .expect("Failed to read expected.log"); + + let mut actual_output_file = NamedTempFile::new().expect("Failed to create temp file"); + + let output = Command::new("cargo") + .args(&["run", "--quiet"]) + .stdin(fs::File::open(input_file).expect("Failed to open test.log")) + .output() + .expect("Failed to run prettify_logs"); + + assert!(output.status.success()); + + actual_output_file + .write_all(&output.stdout) + .expect("Failed to write output"); + + let actual_output = String::from_utf8_lossy(&output.stdout); + + assert_eq!(actual_output.trim(), expected_output.trim(), "Output does not match expected.log"); + + if actual_output.trim() != expected_output.trim() { + eprintln!( + "Test failed! Actual output saved to: {}", + actual_output_file.path().display() + ); + } +}