From 3368727722448f3bb78a89e9138382c32614b98e Mon Sep 17 00:00:00 2001 From: rolv Date: Sat, 8 Nov 2025 15:37:28 +0000 Subject: [PATCH 01/10] feat: support passing data directly to `store` as an alternative to `STDIN` --- src/cli.rs | 4 +++- src/commands/store.rs | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index c2f20a2..3a3d49b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{ffi::OsString, path::PathBuf, str::FromStr}; use clap::{Parser, Subcommand, ValueHint, command}; use regex::Regex; @@ -46,6 +46,8 @@ pub enum Commands { #[derive(Debug, clap::Args)] pub struct StoreArgs { + #[arg(raw = true)] + pub bytes: Option, /// Maximum number of entries to store. /// /// Setting this value to 0 disables the limit. diff --git a/src/commands/store.rs b/src/commands/store.rs index c51dc11..1d4cf18 100644 --- a/src/commands/store.rs +++ b/src/commands/store.rs @@ -1,5 +1,6 @@ use std::{ io::{Read, stdin}, + os::unix::ffi::OsStringExt, path::Path, }; @@ -19,6 +20,7 @@ use crate::{ #[instrument] pub fn execute(path_db: &Path, args: StoreArgs) -> Result<()> { let StoreArgs { + bytes, max_entries, max_entry_age: max_age, max_entry_length: max_bytes, @@ -57,12 +59,19 @@ pub fn execute(path_db: &Path, args: StoreArgs) -> Result<()> { } }; + // Read input passed directly + let buf = if let Some(os_str) = bytes { + os_str.into_vec() + } // Read input from STDIN - let mut buf = vec![]; - stdin() - .read_to_end(&mut buf) - .into_diagnostic() - .context("failed to read from STDIN")?; + else { + let mut buf = vec![]; + stdin() + .read_to_end(&mut buf) + .into_diagnostic() + .context("failed to read from STDIN")?; + buf + }; // No content to store if buf.is_empty() { From 6dd32036c2db6c812e6a625fdfb2702a3249c7e4 Mon Sep 17 00:00:00 2001 From: rolv Date: Sat, 8 Nov 2025 15:39:05 +0000 Subject: [PATCH 02/10] refactor: implement defaults for command arguments --- src/cli.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/cli.rs b/src/cli.rs index 3a3d49b..651686e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -89,6 +89,21 @@ pub struct StoreArgs { pub ignore_pattern: Option>, } +impl Default for StoreArgs { + fn default() -> Self { + Self { + max_entries: defaults::MAX_ENTRIES, + max_entry_age: humantime::Duration::from_str(defaults::MAX_ENTRY_AGE) + .expect("default max entry age should be valid"), + max_entry_length: defaults::MAX_ENTRY_LEN, + min_entry_length: defaults::MIN_ENTRY_LEN, + bytes: None, + store_sensitive: false, + ignore_pattern: None, + } + } +} + #[derive(Debug, clap::Args)] pub struct ListArgs { /// Maximum width in characters for the previews. @@ -107,7 +122,16 @@ pub struct ListArgs { pub reverse: bool, } -#[derive(Debug, clap::Args)] +impl Default for ListArgs { + fn default() -> Self { + Self { + max_preview_width: defaults::MAX_PREVIEW_WIDTH, + reverse: false, + } + } +} + +#[derive(Debug, Default, clap::Args)] pub struct GetDelArgs { /// The selected row from `clipvault list`, or just the ID of the entry. /// From f3e1956d955000f6a20d7791b1fa0909342fe10f Mon Sep 17 00:00:00 2001 From: rolv Date: Sat, 8 Nov 2025 15:40:18 +0000 Subject: [PATCH 03/10] refactor: allow running `get` and `list` without writing to stdout, for benchmarking --- src/commands/get.rs | 63 +++++++++++++++++++++++++++----------------- src/commands/list.rs | 17 +++++++++++- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/commands/get.rs b/src/commands/get.rs index d22a767..024d676 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -17,8 +17,35 @@ use crate::{ utils::ignore_broken_pipe, }; +fn get_entry(path_db: &Path, mut input: String) -> Result { + // Read from STDIN if no argument given + if input.is_empty() { + stdin() + .lock() + .read_to_string(&mut input) + .into_diagnostic() + .context("failed to read STDIN")?; + } + + let id = extract_id(input)?; + let conn = init_db(path_db)?; + get_entry_by_id(&conn, id) +} + +fn get_entry_rel(path_db: &Path, i: isize) -> Result { + let conn = &init_db(path_db)?; + + let len = count_entries(conn)?; + if len == 0 { + return Err(miette!("there are currently no saved clipboard entries")); + } + + let index = wrap_index(len, i); + get_entry_by_position(conn, index) +} + #[tracing::instrument(skip(path_db))] -pub fn execute(path_db: &Path, args: GetDelArgs) -> Result<()> { +fn execute_inner(path_db: &Path, args: GetDelArgs, show_output: bool) -> Result<()> { let GetDelArgs { input, index } = args; assert!( @@ -33,6 +60,11 @@ pub fn execute(path_db: &Path, args: GetDelArgs) -> Result<()> { get_entry(path_db, input) }?; + // Used for benchmarks - don't actually write to stdout + if !show_output { + return Ok(()); + } + // Write to STDOUT let stdout = stdout(); let mut stdout = stdout.lock(); @@ -47,29 +79,12 @@ pub fn execute(path_db: &Path, args: GetDelArgs) -> Result<()> { Ok(()) } -fn get_entry(path_db: &Path, mut input: String) -> Result { - // Read from STDIN if no argument given - if input.is_empty() { - stdin() - .lock() - .read_to_string(&mut input) - .into_diagnostic() - .context("failed to read STDIN")?; - } - - let id = extract_id(input)?; - let conn = init_db(path_db)?; - get_entry_by_id(&conn, id) +#[tracing::instrument(skip(path_db))] +pub fn execute(path_db: &Path, args: GetDelArgs) -> Result<()> { + execute_inner(path_db, args, true) } -fn get_entry_rel(path_db: &Path, i: isize) -> Result { - let conn = &init_db(path_db)?; - - let len = count_entries(conn)?; - if len == 0 { - return Err(miette!("there are currently no saved clipboard entries")); - } - - let index = wrap_index(len, i); - get_entry_by_position(conn, index) +#[tracing::instrument(skip(path_db))] +pub fn execute_without_output(path_db: &Path, args: GetDelArgs) -> Result<()> { + execute_inner(path_db, args, false) } diff --git a/src/commands/list.rs b/src/commands/list.rs index fbdc115..4ea9121 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -73,7 +73,7 @@ fn preview(id: u64, data: &[u8], width: usize) -> String { } #[tracing::instrument(skip(path_db))] -pub fn execute(path_db: &Path, args: ListArgs) -> Result<()> { +fn execute_inner(path_db: &Path, args: ListArgs, show_output: bool) -> Result<()> { let ListArgs { max_preview_width, reverse, @@ -109,6 +109,11 @@ pub fn execute(path_db: &Path, args: ListArgs) -> Result<()> { .collect::>() .join("\n"); + // Used for benchmarks - don't actually write to stdout + if !show_output { + return Ok(()); + } + let mut stdout = stdout().lock(); ignore_broken_pipe(writeln!(&mut stdout, "{output}",)) .into_diagnostic() @@ -119,3 +124,13 @@ pub fn execute(path_db: &Path, args: ListArgs) -> Result<()> { Ok(()) } + +#[tracing::instrument(skip(path_db))] +pub fn execute(path_db: &Path, args: ListArgs) -> Result<()> { + execute_inner(path_db, args, true) +} + +#[tracing::instrument(skip(path_db))] +pub fn execute_without_output(path_db: &Path, args: ListArgs) -> Result<()> { + execute_inner(path_db, args, false) +} From 68a198bf40993f232a483f9ae20c73bf4f1a3356 Mon Sep 17 00:00:00 2001 From: rolv Date: Sat, 8 Nov 2025 15:41:52 +0000 Subject: [PATCH 04/10] chore: implement basic benchmarks --- Cargo.lock | 195 +++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 5 ++ benches/base.rs | 100 +++++++++++++++++++++++++ 3 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 benches/base.rs diff --git a/Cargo.lock b/Cargo.lock index fe16822..b487b2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,12 @@ dependencies = [ "equator", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.6.21" @@ -266,6 +272,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.2.44" @@ -294,6 +306,33 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" version = "4.5.51" @@ -343,6 +382,7 @@ dependencies = [ "base64", "clap", "content_inspector", + "criterion", "dirs", "humantime", "image", @@ -405,6 +445,39 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +dependencies = [ + "cast", + "itertools 0.13.0", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -963,6 +1036,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -979,6 +1061,16 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1259,6 +1351,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "option-ext" version = "0.2.0" @@ -1304,6 +1402,34 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "png" version = "0.18.0" @@ -1555,7 +1681,7 @@ dependencies = [ "built", "cfg-if", "interpolate_name", - "itertools", + "itertools 0.12.1", "libc", "libfuzzer-sys", "log", @@ -1719,6 +1845,21 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" version = "1.0.228" @@ -1749,6 +1890,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -2017,6 +2171,16 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "toml" version = "0.8.23" @@ -2216,6 +2380,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -2276,12 +2450,31 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "weezl" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows-link" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 9a8eece..0b51920 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,11 @@ base64 = "0.22" tempfile = "3.23" proptest = "1.9" insta = "1.43" +criterion = { version = "0.7", features = ["html_reports"] } + +[[bench]] +name = "base" +harness = false [profile.release] strip = true diff --git a/benches/base.rs b/benches/base.rs new file mode 100644 index 0000000..ab15601 --- /dev/null +++ b/benches/base.rs @@ -0,0 +1,100 @@ +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; + +use std::ffi::OsString; +use std::str::FromStr; +use std::sync::LazyLock; +use std::time::Duration; + +use clipvault::cli::{GetDelArgs, ListArgs, StoreArgs}; +use clipvault::commands::{get, list, store}; +use clipvault::defaults; +use tempfile::NamedTempFile; + +/// Get temporary file for DB. +fn get_temp() -> NamedTempFile { + NamedTempFile::new().expect("couldn't create tempfile") +} + +static DB: LazyLock = LazyLock::new(|| { + let db = get_temp(); + for n in 0..defaults::MAX_ENTRIES { + let bytes = OsString::from_str("0".repeat(n).as_ref()).unwrap(); + let args = StoreArgs { + bytes: Some(bytes), + ..Default::default() + }; + store::execute(db.path(), args).expect("failed to store"); + } + db +}); + +fn store(n: usize) { + let db = get_temp(); + + let bytes = OsString::from_str("a".repeat(n).as_ref()).unwrap(); + for _ in 0..n { + let args = StoreArgs { + bytes: Some(bytes.clone()), + ..Default::default() + }; + store::execute(db.path(), args).expect("failed to store"); + } +} + +fn list(n: usize) { + let path_db = DB.path(); + + let args = ListArgs { + max_preview_width: n, + ..Default::default() + }; + + list::execute_without_output(path_db, args).expect("failed to list"); +} + +fn get(n: isize) { + let path_db = DB.path(); + + let args = GetDelArgs { + input: String::new(), + index: Some(n), + }; + + get::execute_without_output(path_db, args).expect("failed to get"); +} + +fn criterion_benchmark(c: &mut Criterion) { + let mut g = c.benchmark_group("base"); + g.warm_up_time(Duration::from_secs(1)) + .significance_level(0.01); + + // STORE + for n in [1, 10, 100, 1000] { + g.bench_with_input(BenchmarkId::new("store", n), &n, |b, i| { + b.iter(|| store(*i)); + }); + } + + // LIST + for n in [1, 5, 10, 25, 50, 100, 1000] { + g.bench_with_input(BenchmarkId::new("list", n), &n, |b, i| { + b.iter(|| list(*i)); + }); + } + + // GET + for n in [-100000, -1, 0, 1, 100000] { + g.bench_with_input(BenchmarkId::new("get", n), &n, |b, i| { + b.iter(|| { + for _ in 0..100 { + get(*i) + } + }); + }); + } + + g.finish(); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); From e6ae13473b74782955081f7e2e0e721c6cab7ca4 Mon Sep 17 00:00:00 2001 From: rolv Date: Sat, 8 Nov 2025 22:33:02 +0000 Subject: [PATCH 05/10] ci: benchmark comparison for PRs --- .github/workflows/bench.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/workflows/bench.yml diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml new file mode 100644 index 0000000..9acc0f5 --- /dev/null +++ b/.github/workflows/bench.yml @@ -0,0 +1,12 @@ +on: [pull_request] +name: Benchmark PRs +jobs: + bench: + name: Benchmark + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: boa-dev/criterion-compare-action@v3 + with: + branchName: ${{ github.base_ref }} + token: ${{ secrets.GITHUB_TOKEN }} From c3e6167929c109ce2f0cfbbed34fc48e75366f5b Mon Sep 17 00:00:00 2001 From: rolv Date: Sat, 8 Nov 2025 22:39:57 +0000 Subject: [PATCH 06/10] chore: fix for criterion --- Cargo.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 0b51920..66b6a97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,14 @@ proptest = "1.9" insta = "1.43" criterion = { version = "0.7", features = ["html_reports"] } +[[bin]] +name = "clipvault" +path = "src/main.rs" +bench = false + +[lib] +bench = false + [[bench]] name = "base" harness = false From 92bbc73e32ccbfe357646cad00d2234bf2971f2b Mon Sep 17 00:00:00 2001 From: rolv Date: Sat, 8 Nov 2025 23:15:53 +0000 Subject: [PATCH 07/10] refactor: use `divan` and `CodSpeed` --- .github/workflows/bench.yml | 31 +++- Cargo.lock | 346 +++++++++++++++++++----------------- Cargo.toml | 10 +- benches/base.rs | 54 ++---- 4 files changed, 216 insertions(+), 225 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 9acc0f5..4710a48 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -1,12 +1,29 @@ -on: [pull_request] -name: Benchmark PRs +name: CodSpeed benchmarks + +on: + push: + branches: + - "main" + pull_request: + workflow_dispatch: + jobs: - bench: - name: Benchmark + benchmarks: + name: Run benchmarks runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: boa-dev/criterion-compare-action@v3 + - name: Setup rust toolchain, cache and cargo-codspeed binary + uses: moonrepo/setup-rust@v1 with: - branchName: ${{ github.base_ref }} - token: ${{ secrets.GITHUB_TOKEN }} + channel: stable + cache-target: release + bins: cargo-codspeed + - name: Build the benchmark target(s) + run: cargo codspeed build + - name: Run the benchmarks + uses: CodSpeedHQ/action@v4 + with: + mode: instrumentation + run: cargo codspeed run + token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index b487b2f..088d458 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,12 +35,6 @@ dependencies = [ "equator", ] -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - [[package]] name = "anstream" version = "0.6.21" @@ -97,6 +91,15 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arbitrary" version = "1.4.2" @@ -272,12 +275,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - [[package]] name = "cc" version = "1.2.44" @@ -307,31 +304,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" @@ -353,6 +329,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -381,8 +358,8 @@ dependencies = [ "assert_cmd", "base64", "clap", + "codspeed-divan-compat", "content_inspector", - "criterion", "dirs", "humantime", "image", @@ -403,6 +380,66 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "codspeed" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b847e05a34be5c38f3f2a5052178a3bd32e6b5702f3ea775efde95c483a539" +dependencies = [ + "anyhow", + "cc", + "colored", + "getrandom 0.2.16", + "glob", + "libc", + "nix", + "serde", + "serde_json", + "statrs", +] + +[[package]] +name = "codspeed-divan-compat" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f0e9fe5eaa39995ec35e46407f7154346cc25bd1300c64c21636f3d00cb2cc" +dependencies = [ + "clap", + "codspeed", + "codspeed-divan-compat-macros", + "codspeed-divan-compat-walltime", + "regex", +] + +[[package]] +name = "codspeed-divan-compat-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c8babf2a40fd2206a2e030cf020d0d58144cd56e1dc408bfba02cdefb08b4f" +dependencies = [ + "divan-macros", + "itertools 0.14.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "codspeed-divan-compat-walltime" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f26092328e12a36704ffc552f379c6405dd94d3149970b79b22d371717c2aae" +dependencies = [ + "cfg-if", + "clap", + "codspeed", + "condtype", + "divan-macros", + "libc", + "regex-lite", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -415,6 +452,22 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "condtype" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" + [[package]] name = "console" version = "0.15.11" @@ -445,39 +498,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "criterion" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "itertools 0.13.0", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" -dependencies = [ - "cast", - "itertools 0.13.0", -] - [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -571,6 +591,17 @@ dependencies = [ "syn", ] +[[package]] +name = "divan-macros" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc51d98e636f5e3b0759a39257458b22619cac7e96d932da6eeb052891bb67c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.15.0" @@ -775,6 +806,12 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "half" version = "2.7.1" @@ -1038,9 +1075,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -1061,16 +1098,6 @@ dependencies = [ "libc", ] -[[package]] -name = "js-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1244,6 +1271,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "8.0.0" @@ -1351,12 +1390,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - [[package]] name = "option-ext" version = "0.2.0" @@ -1402,34 +1435,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - [[package]] name = "png" version = "0.18.0" @@ -1507,6 +1512,15 @@ dependencies = [ "yansi", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.7", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -1771,6 +1785,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" + [[package]] name = "regex-syntax" version = "0.8.8" @@ -1851,15 +1871,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "serde" version = "1.0.228" @@ -1960,6 +1971,16 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "statrs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a3fe7c28c6512e766b0874335db33c94ad7b8f9054228ae1c2abd47ce7d335e" +dependencies = [ + "approx", + "num-traits", +] + [[package]] name = "strsim" version = "0.11.1" @@ -2171,16 +2192,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "toml" version = "0.8.23" @@ -2189,8 +2200,8 @@ checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", - "toml_datetime", - "toml_edit", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", ] [[package]] @@ -2202,6 +2213,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -2211,7 +2231,28 @@ dependencies = [ "indexmap", "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.11", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap", + "toml_datetime 0.7.3", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ "winnow", ] @@ -2380,16 +2421,6 @@ dependencies = [ "libc", ] -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -2450,31 +2481,12 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "web-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "weezl" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "windows-link" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 66b6a97..99422bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,15 +49,7 @@ base64 = "0.22" tempfile = "3.23" proptest = "1.9" insta = "1.43" -criterion = { version = "0.7", features = ["html_reports"] } - -[[bin]] -name = "clipvault" -path = "src/main.rs" -bench = false - -[lib] -bench = false +divan = { version = "4.1.0", package = "codspeed-divan-compat" } [[bench]] name = "base" diff --git a/benches/base.rs b/benches/base.rs index ab15601..9e62352 100644 --- a/benches/base.rs +++ b/benches/base.rs @@ -1,9 +1,6 @@ -use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; - use std::ffi::OsString; use std::str::FromStr; use std::sync::LazyLock; -use std::time::Duration; use clipvault::cli::{GetDelArgs, ListArgs, StoreArgs}; use clipvault::commands::{get, list, store}; @@ -28,6 +25,7 @@ static DB: LazyLock = LazyLock::new(|| { db }); +#[divan::bench(args = [1, 10, 100, 1000])] fn store(n: usize) { let db = get_temp(); @@ -41,6 +39,7 @@ fn store(n: usize) { } } +#[divan::bench(args = [1, 5, 10, 25, 50, 100, 1000])] fn list(n: usize) { let path_db = DB.path(); @@ -52,49 +51,20 @@ fn list(n: usize) { list::execute_without_output(path_db, args).expect("failed to list"); } +#[divan::bench(args = [-100000, -1, 0, 1, 100000])] fn get(n: isize) { let path_db = DB.path(); - let args = GetDelArgs { - input: String::new(), - index: Some(n), - }; - - get::execute_without_output(path_db, args).expect("failed to get"); -} - -fn criterion_benchmark(c: &mut Criterion) { - let mut g = c.benchmark_group("base"); - g.warm_up_time(Duration::from_secs(1)) - .significance_level(0.01); - - // STORE - for n in [1, 10, 100, 1000] { - g.bench_with_input(BenchmarkId::new("store", n), &n, |b, i| { - b.iter(|| store(*i)); - }); - } - - // LIST - for n in [1, 5, 10, 25, 50, 100, 1000] { - g.bench_with_input(BenchmarkId::new("list", n), &n, |b, i| { - b.iter(|| list(*i)); - }); - } + for _ in 0..100 { + let args = GetDelArgs { + input: String::new(), + index: Some(n), + }; - // GET - for n in [-100000, -1, 0, 1, 100000] { - g.bench_with_input(BenchmarkId::new("get", n), &n, |b, i| { - b.iter(|| { - for _ in 0..100 { - get(*i) - } - }); - }); + get::execute_without_output(path_db, args).expect("failed to get"); } - - g.finish(); } -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); +fn main() { + divan::main(); +} From 115de476e491564abc021a130eb870cc53ec5782 Mon Sep 17 00:00:00 2001 From: rolv Date: Sat, 8 Nov 2025 23:41:57 +0000 Subject: [PATCH 08/10] refactor: tune divan benchmarks --- benches/base.rs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/benches/base.rs b/benches/base.rs index 9e62352..4f640b8 100644 --- a/benches/base.rs +++ b/benches/base.rs @@ -25,21 +25,20 @@ static DB: LazyLock = LazyLock::new(|| { db }); -#[divan::bench(args = [1, 10, 100, 1000])] +#[divan::bench(args = [1, 10, 100, 10_000, 100_000, 1_000_000], sample_size=10)] fn store(n: usize) { let db = get_temp(); let bytes = OsString::from_str("a".repeat(n).as_ref()).unwrap(); - for _ in 0..n { - let args = StoreArgs { - bytes: Some(bytes.clone()), - ..Default::default() - }; - store::execute(db.path(), args).expect("failed to store"); - } + let args = StoreArgs { + bytes: Some(bytes), + ..Default::default() + }; + + store::execute(db.path(), args).expect("failed to store"); } -#[divan::bench(args = [1, 5, 10, 25, 50, 100, 1000])] +#[divan::bench(args = [1, 5, 10, 25, 50, 100, 1000], sample_size=10)] fn list(n: usize) { let path_db = DB.path(); @@ -51,18 +50,16 @@ fn list(n: usize) { list::execute_without_output(path_db, args).expect("failed to list"); } -#[divan::bench(args = [-100000, -1, 0, 1, 100000])] +#[divan::bench(args = [-100000, -1, 0, 1, 100000], sample_size=100)] fn get(n: isize) { let path_db = DB.path(); - for _ in 0..100 { - let args = GetDelArgs { - input: String::new(), - index: Some(n), - }; + let args = GetDelArgs { + input: String::new(), + index: Some(n), + }; - get::execute_without_output(path_db, args).expect("failed to get"); - } + get::execute_without_output(path_db, args).expect("failed to get"); } fn main() { From b4d4d1ab2bd7e6b036c5082492bc8e4fbd9f0f65 Mon Sep 17 00:00:00 2001 From: rolv Date: Sun, 9 Nov 2025 15:46:32 +0000 Subject: [PATCH 09/10] refactor: assert that `without_output` variants of `list` and `get` commands are not run in production --- src/commands/get.rs | 5 +++++ src/commands/list.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/commands/get.rs b/src/commands/get.rs index 024d676..a37d61d 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -84,7 +84,12 @@ pub fn execute(path_db: &Path, args: GetDelArgs) -> Result<()> { execute_inner(path_db, args, true) } +#[doc(hidden)] #[tracing::instrument(skip(path_db))] pub fn execute_without_output(path_db: &Path, args: GetDelArgs) -> Result<()> { + assert!( + !cfg!(debug_assertions), + "Not intended to run in production code" + ); execute_inner(path_db, args, false) } diff --git a/src/commands/list.rs b/src/commands/list.rs index 4ea9121..7633ad1 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -130,7 +130,12 @@ pub fn execute(path_db: &Path, args: ListArgs) -> Result<()> { execute_inner(path_db, args, true) } +#[doc(hidden)] #[tracing::instrument(skip(path_db))] pub fn execute_without_output(path_db: &Path, args: ListArgs) -> Result<()> { + assert!( + !cfg!(debug_assertions), + "Not intended to run in production code" + ); execute_inner(path_db, args, false) } From 6faabd8a0136585758894036e33394909105b97f Mon Sep 17 00:00:00 2001 From: rolv Date: Sun, 9 Nov 2025 15:47:48 +0000 Subject: [PATCH 10/10] refactor: drop support for passing input directly (incompatability with argifles) --- benches/base.rs | 22 +++++++--------------- src/cli.rs | 5 +---- src/commands/store.rs | 18 +++++++++--------- 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/benches/base.rs b/benches/base.rs index 4f640b8..6fc681d 100644 --- a/benches/base.rs +++ b/benches/base.rs @@ -1,5 +1,4 @@ -use std::ffi::OsString; -use std::str::FromStr; +use std::io::Cursor; use std::sync::LazyLock; use clipvault::cli::{GetDelArgs, ListArgs, StoreArgs}; @@ -15,12 +14,9 @@ fn get_temp() -> NamedTempFile { static DB: LazyLock = LazyLock::new(|| { let db = get_temp(); for n in 0..defaults::MAX_ENTRIES { - let bytes = OsString::from_str("0".repeat(n).as_ref()).unwrap(); - let args = StoreArgs { - bytes: Some(bytes), - ..Default::default() - }; - store::execute(db.path(), args).expect("failed to store"); + let args = StoreArgs::default(); + let bytes = "0".repeat(n).into_bytes(); + store::execute_with_source(db.path(), args, Cursor::new(bytes)).expect("failed to store"); } db }); @@ -29,13 +25,9 @@ static DB: LazyLock = LazyLock::new(|| { fn store(n: usize) { let db = get_temp(); - let bytes = OsString::from_str("a".repeat(n).as_ref()).unwrap(); - let args = StoreArgs { - bytes: Some(bytes), - ..Default::default() - }; - - store::execute(db.path(), args).expect("failed to store"); + let args = StoreArgs::default(); + let bytes = "0".repeat(n).into_bytes(); + store::execute_with_source(db.path(), args, Cursor::new(bytes)).expect("failed to store"); } #[divan::bench(args = [1, 5, 10, 25, 50, 100, 1000], sample_size=10)] diff --git a/src/cli.rs b/src/cli.rs index 651686e..59b9d5f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsString, path::PathBuf, str::FromStr}; +use std::{path::PathBuf, str::FromStr}; use clap::{Parser, Subcommand, ValueHint, command}; use regex::Regex; @@ -46,8 +46,6 @@ pub enum Commands { #[derive(Debug, clap::Args)] pub struct StoreArgs { - #[arg(raw = true)] - pub bytes: Option, /// Maximum number of entries to store. /// /// Setting this value to 0 disables the limit. @@ -97,7 +95,6 @@ impl Default for StoreArgs { .expect("default max entry age should be valid"), max_entry_length: defaults::MAX_ENTRY_LEN, min_entry_length: defaults::MIN_ENTRY_LEN, - bytes: None, store_sensitive: false, ignore_pattern: None, } diff --git a/src/commands/store.rs b/src/commands/store.rs index 1d4cf18..9f75d03 100644 --- a/src/commands/store.rs +++ b/src/commands/store.rs @@ -1,6 +1,5 @@ use std::{ io::{Read, stdin}, - os::unix::ffi::OsStringExt, path::Path, }; @@ -19,8 +18,13 @@ use crate::{ #[instrument] pub fn execute(path_db: &Path, args: StoreArgs) -> Result<()> { + execute_with_source(path_db, args, stdin()) +} + +#[doc(hidden)] +#[instrument(skip(source))] +pub fn execute_with_source(path_db: &Path, args: StoreArgs, mut source: impl Read) -> Result<()> { let StoreArgs { - bytes, max_entries, max_entry_age: max_age, max_entry_length: max_bytes, @@ -59,14 +63,10 @@ pub fn execute(path_db: &Path, args: StoreArgs) -> Result<()> { } }; - // Read input passed directly - let buf = if let Some(os_str) = bytes { - os_str.into_vec() - } - // Read input from STDIN - else { + // Read input using given source - this should be STDIN for production code + let buf = { let mut buf = vec![]; - stdin() + source .read_to_end(&mut buf) .into_diagnostic() .context("failed to read from STDIN")?;