Skip to main content

heddle_cli_args/cli/cli_args/
commands_query.rs

1// SPDX-License-Identifier: Apache-2.0
2//! `heddle query` — structured query over the operation log.
3
4use clap::Args;
5
6use super::HistoricalRevisionArgs;
7
8/// Line-by-line attribution for a tracked file (`heddle blame <path>`).
9#[derive(Clone, Debug, Args)]
10pub struct BlameArgs {
11    /// Tracked file to attribute.
12    #[arg(value_name = "PATH")]
13    pub path: String,
14    #[command(flatten)]
15    pub revision: HistoricalRevisionArgs,
16    /// Include applicable context annotations.
17    #[arg(long)]
18    pub context: bool,
19}
20
21#[derive(Clone, Debug, Args)]
22pub struct QueryArgs {
23    /// Show line-by-line attribution for a tracked file.
24    #[arg(long, value_name = "FILE")]
25    pub attribution: Option<String>,
26    /// State to inspect with `--attribution`. Accepts short or full
27    /// state IDs, marker names, `HEAD`, `@`, or `HEAD~N`.
28    #[arg(long, requires = "attribution")]
29    pub state: Option<String>,
30    /// Include applicable context annotations with `--attribution`.
31    #[arg(long, requires = "attribution")]
32    pub context: bool,
33    /// Filter by actor email.
34    #[arg(long)]
35    pub actor: Option<String>,
36    /// Lower bound. Accepts RFC3339 (`2026-05-04T12:00:00Z`) or
37    /// humantime (`1h`, `2d`, `30m`).
38    #[arg(long)]
39    pub since: Option<String>,
40    /// Upper bound, same formats as `--since`.
41    #[arg(long)]
42    pub until: Option<String>,
43    /// Filter by signal kind (e.g. `novelty`, `invariant_adjacency`).
44    #[arg(long)]
45    pub signal: Option<String>,
46    /// Filter by symbol (free-form `<file>:<symbol>` string).
47    #[arg(long)]
48    pub symbol: Option<String>,
49    /// Filter by thread name.
50    #[arg(long)]
51    pub thread: Option<String>,
52    /// Restrict to specific oplog verbs. Repeat to allow multiple.
53    /// `capture` matches the stored `snapshot` verb; matching is case-insensitive.
54    #[arg(long = "verb")]
55    pub verbs: Vec<String>,
56    /// Maximum hits to return.
57    #[arg(long, default_value = "100")]
58    pub limit: u32,
59    /// Include checkpoint entries (excluded by default).
60    #[arg(long)]
61    pub include_checkpoints: bool,
62}
63
64#[cfg(test)]
65mod tests {
66    use clap::Parser;
67
68    use crate::cli::{Cli, Commands};
69
70    #[test]
71    fn blame_parses_path_state_and_context() {
72        match Cli::try_parse_from(["heddle", "blame", "src/auth.rs"])
73            .expect("blame path")
74            .command
75        {
76            Commands::Blame(args) => {
77                assert_eq!(args.path, "src/auth.rs");
78                assert!(args.revision.state.is_none());
79                assert!(!args.context);
80            }
81            _ => panic!("expected blame"),
82        }
83        match Cli::try_parse_from([
84            "heddle",
85            "blame",
86            "src/auth.rs",
87            "--state",
88            "HEAD",
89            "--context",
90        ])
91        .expect("blame flags")
92        .command
93        {
94            Commands::Blame(args) => {
95                assert_eq!(args.revision.state.as_deref(), Some("HEAD"));
96                assert!(args.context);
97            }
98            _ => panic!("expected blame"),
99        }
100    }
101}