Repository: atuinsh/atuin (language: rust)

Issue:
[Bug]: atuin stats panics when faced with unusual string in history
### What did you expect to happen?

No panic! ;]

### What happened?

When running `atuin stats` it panics with:

```
❯ RUST_BACKTRACE=1 atuin stats
thread 'main' panicked at atuin/src/command/client/stats.rs:56:41:
begin <= end (1 <= 0) when slicing `| awk '{print $1}' | R --no-echo -e 'x <- scan(file="stdin", quiet=TRUE); summary(x)'`
stack backtrace:
   0: _rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::str::slice_error_fail_rt
   3: core::str::slice_error_fail
   4: atuin::command::client::stats::compute_stats
   5: atuin::command::client::Cmd::run_inner::{{closure}}
   6: tokio::runtime::scheduler::current_thread::Context::enter
   7: tokio::runtime::context::set_scheduler
   8: tokio::runtime::scheduler::current_thread::CoreGuard::block_on
   9: tokio::runtime::context::runtime::enter_runtime
  10: tokio::runtime::runtime::Runtime::block_on
  11: atuin::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```

Now that string `| awk '{print $1}' | R --no-echo -e 'x <- scan(file="stdin", quiet=TRUE); summary(x)'` is actually in my history like that (it's the "entire" command), so obviously some copy-pasta failure on my part, but I don't think it should make `atuin` panic (maybe due to starting with a `|`?).

### Atuin doctor output

```yaml
Atuin Doctor
Checking for diagnostics


Please include the output below with any bug reports or issues

atuin:
  version: 18.1.0
  sync:
    cloud: false
    records: true
    auto_sync: true
    last_sync: 2024-03-14 18:05:58.081633 +00:00:00
shell:
  name: zsh
  plugins:
  - atuin
system:
  os: Darwin
  arch: x86_64
  version: 14.3.1
  disks:
  - name: Macintosh HD
    filesystem: apfs
  - name: Macintosh HD
    filesystem: apfs
```


### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion / hints:
seconding this- here's another fun string this happens with.
```console
$ RUST_BACKTRACE=1 atuin stats
thread 'main' panicked at atuin/src/command/client/stats.rs:56:41:
begin <= end (1 <= 0) when slicing `|_|  |____||___||_|  |_|  |___||_|_`
stack backtrace:
   0:        0x1054a29d0 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h61949099c17bb561
   1:        0x1054ca8e0 - core::fmt::write::he0c0819610fe7c82
   2:        0x105487c8c - std::io::Write::write_fmt::h14dadda6958822c3
   3:        0x1054a2824 - std::sys_common::backtrace::print::h10166cbeffac9d38
   4:        0x1054a3360 - std::panicking::default_hook::{{closure}}::hfec7fca779e11f3b
   5:        0x1054a30e0 - std::panicking::default_hook::h26402d2c6670ffd0
   6:        0x1054a39d4 - std::panicking::rust_panic_with_hook::hb00dd38969b5a277
   7:        0x1054a378c - std::panicking::begin_panic_handler::{{closure}}::hc86edf66ba485638
   8:        0x1054a2c04 - std::sys_common::backtrace::__rust_end_short_backtrace::h24f57ebe971b5eac
   9:        0x1054a3514 - _rust_begin_unwind
  10:        0x1055123e8 - core::panicking::panic_fmt::h21b3a72f47844886
  11:        0x1054c6c10 - core::str::slice_error_fail_rt::h0bcfaeca513f69c4
  12:        0x105512790 - core::str::slice_error_fail::h2e034b398f7b75a7
  13:        0x104c3f69c - atuin::command::client::stats::compute_stats::h309a358b9b61ecd5
  14:        0x104a24714 - atuin::command::client::Cmd::run_inner::{{closure}}::h4a9567b4490695de
  15:        0x104b13448 - tokio::runtime::scheduler::current_thread::CoreGuard::block_on::h351c1c892460edf6
  16:        0x104c38318 - tokio::runtime::context::runtime::enter_runtime::hef50c501bad43a82
  17:        0x104b6e14c - tokio::runtime::runtime::Runtime::block_on::h7d3459fe69753074
  18:        0x104bd38a4 - atuin::command::client::Cmd::run::h588a5ab129b29fa6
  19:        0x104c400a4 - atuin::command::AtuinCmd::run::hf663edac3a0da924
  20:        0x104bd3d70 - atuin::main::h61cb3fcec8322f37
  21:        0x104b96218 - std::sys_common::backtrace::__rust_begin_short_backtrace::he76aba5053fdcc16
  22:        0x104c242b8 - std::rt::lang_start::{{closure}}::h024e6bed810fd961
  23:        0x1054a3400 - std::panicking::try::ha883230fd73a158c
  24:        0x105485fc0 - std::rt::lang_start_internal::ha86617696da0c619
  25:        0x104bd8c0c - _main
```

installed via nixpkgs/nix-darwin. atuin doctor output:

```yaml
atuin:
  version: 18.1.0
  sync:
    cloud: false
    records: false
    auto_sync: true
    last_sync: 2024-03-24 19:14:04.454307 +00:00:00
shell:
  name: zsh
  plugins:
  - atuin
system:
  os: Darwin
  arch: arm64
  version: '14.4'
  disks:
  - name: Macintosh HD
    filesystem: apfs
  - name: Macintosh HD
    filesystem: apfs
```
this helped me track down and a similar panic, with `| landscape` as a history entry. I deleted the entry in atuin to fix my stats output.
Got something very similar:
```
atuin stats       
thread 'main' panicked at atuin/src/command/client/stats.rs:56:41:
begin <= end (1 <= 0) when slicing `| wc -l`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

It would be helpful if it printed the entire command that caused the problem, so I could delete it.
something similar 

![image](https://github.com/atuinsh/atuin/assets/1120692/7ca9cd61-c402-4507-845a-41158e0d9fef)

` RUST_BACKTRACE=full atuin stats
thread 'main' panicked at atuin/src/command/client/stats.rs:56:41:
begin <= end (1 <= 0) when slicing `| HTTPCode=%{http_code}\n\n"`
stack backtrace:
   0:     0x562b9d838c96 - <unknown>
   1:     0x562b9d868f20 - <unknown>
   2:     0x562b9d834f6f - <unknown>`
Appears to happen for all commands starting with a pipe/| character for me (I used prune with a history_filter on the offending string in my config.toml to work around the issue).

Here's my backtrace output:
```
❯ atuin stats
thread 'main' panicked at atuin/src/command/client/stats.rs:56:41:
begin <= end (1 <= 0) when slicing `| grep -v CHOST`
stack backtrace:
   0:        0x105397884 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hfb3c5255bdb4eac9
   1:        0x1053cc6a4 - core::fmt::write::h4f9b26a431923163
   2:        0x10539d8a8 - std::io::Write::write_fmt::h65a3848fda965d9a
   3:        0x1053976b4 - std::sys_common::backtrace::print::hfb8e68d5340a5773
   4:        0x1053adb90 - std::panicking::default_hook::{{closure}}::h3c1629cb3dea24df
   5:        0x1053ad918 - std::panicking::default_hook::h5b4a788645c74387
   6:        0x1053adf9c - std::panicking::rust_panic_with_hook::h2f1b18b39b6242c7
   7:        0x105397d74 - std::panicking::begin_panic_handler::{{closure}}::hd6c9d27d8f83f59d
   8:        0x105397ab8 - std::sys_common::backtrace::__rust_end_short_backtrace::hb3b76dda55c5a574
   9:        0x1053add24 - _rust_begin_unwind
  10:        0x10541fd60 - core::panicking::panic_fmt::h737710d5a381d17b
  11:        0x1053cf9a0 - core::str::slice_error_fail_rt::h4b74b83a2a7f6848
  12:        0x1054200b4 - core::str::slice_error_fail::h63b632dfbbc1ab21
  13:        0x104b2a234 - atuin::command::client::stats::compute_stats::hda9c3aa01b72b14e
  14:        0x10495420c - atuin::command::client::Cmd::run_inner::{{closure}}::ha0d80b7b53df7bfe
  15:        0x1049e9670 - tokio::runtime::scheduler::current_thread::Context::enter::he4718aa49284b4be
  16:        0x104abeb60 - tokio::runtime::context::scoped::Scoped<T>::set::h3ad6c66c16925748
  17:        0x1049e984c - tokio::runtime::scheduler::current_thread::CoreGuard::block_on::ha97983b83be644af
  18:        0x104ac06e0 - tokio::runtime::context::runtime::enter_runtime::h891287b6d7d34412
  19:        0x104a38ac4 - atuin::command::client::Cmd::run::h1c7da15a92888fd9
  20:        0x104b2b3cc - atuin::main::h45ddc4795688d199
  21:        0x104a58bb0 - std::sys_common::backtrace::__rust_begin_short_backtrace::haad479e4b60591cd
  22:        0x104a01e3c - std::rt::lang_start::{{closure}}::h862dbd93209a848c
  23:        0x1053adc1c - std::panicking::try::he7b2dbdb2ebe3a2a
  24:        0x105399730 - std::rt::lang_start_internal::h3cdb23022df8f974
  25:        0x104b453a8 - _main
```

Fix patch:
diff --git a/crates/atuin-history/src/stats.rs b/crates/atuin-history/src/stats.rs
index ab4127dfcdb..b73a5dbbb5a 100644
--- a/crates/atuin-history/src/stats.rs
+++ b/crates/atuin-history/src/stats.rs
@@ -92,7 +92,7 @@ fn split_at_pipe(command: &str) -> Vec<&str> {
             "\\" => if graphemes.next().is_some() {},
             "|" => {
                 if !quoted {
-                    if command[start..].starts_with('|') {
+                    if current > start && command[start..].starts_with('|') {
                         start += 1;
                     }
                     result.push(&command[start..current]);
@@ -396,4 +396,20 @@ mod tests {
             ["git commit -m \"🚀\""]
         );
     }
+
+    #[test]
+    fn starts_with_pipe() {
+        assert_eq!(
+            split_at_pipe("| sed 's/[0-9a-f]//g'"),
+            ["", " sed 's/[0-9a-f]//g'"]
+        );
+    }
+
+    #[test]
+    fn starts_with_spaces_and_pipe() {
+        assert_eq!(
+            split_at_pipe("  | sed 's/[0-9a-f]//g'"),
+            ["  ", " sed 's/[0-9a-f]//g'"]
+        );
+    }
 }


Test patch: