Skip to main content

heddle_cli_args/cli/cli_args/
commands_discuss.rs

1// SPDX-License-Identifier: Apache-2.0
2//! `heddle discuss` — durable repository collaboration.
3
4use clap::{ArgGroup, Args, Subcommand};
5
6#[derive(Clone, Debug, Subcommand)]
7pub enum DiscussCommands {
8    /// Open a discussion anchored to a symbol.
9    Open(DiscussOpenArgs),
10    /// Append a durable turn to a discussion.
11    Append(DiscussAppendArgs),
12    /// Resolve a discussion.
13    Resolve(DiscussResolveArgs),
14    /// Reopen a resolved discussion.
15    Reopen(DiscussReopenArgs),
16    /// List repository discussions.
17    List(DiscussListArgs),
18    /// Show one discussion and its causal heads.
19    Show(DiscussShowArgs),
20    /// Replay hosted discussion events after the local watermark, then go live.
21    Wait(DiscussWaitArgs),
22}
23
24#[derive(Clone, Debug, Args)]
25#[command(
26    group(
27        ArgGroup::new("positional_open")
28            .args(["file", "symbol", "body"])
29            .multiple(true)
30            .conflicts_with("named_open")
31    ),
32    group(
33        ArgGroup::new("named_open")
34            .args(["file_flag", "symbol_flag", "body_flag"])
35            .multiple(true)
36    )
37)]
38pub struct DiscussOpenArgs {
39    /// Path of the file containing the symbol.
40    #[arg(
41        value_name = "FILE",
42        required_unless_present_all = ["file_flag", "symbol_flag", "body_flag"]
43    )]
44    pub file: Option<String>,
45    /// Symbol name (for example `Repository::open`).
46    #[arg(
47        value_name = "SYMBOL",
48        required_unless_present_all = ["file_flag", "symbol_flag", "body_flag"]
49    )]
50    pub symbol: Option<String>,
51    /// First turn of the discussion.
52    #[arg(
53        value_name = "BODY",
54        required_unless_present_all = ["file_flag", "symbol_flag", "body_flag"]
55    )]
56    pub body: Option<String>,
57    /// Path of the file containing the symbol (named alternative to `<FILE>`).
58    #[arg(
59        long = "file",
60        value_name = "FILE",
61        required_unless_present_all = ["file", "symbol", "body"]
62    )]
63    pub file_flag: Option<String>,
64    /// Symbol name (named alternative to `<SYMBOL>`).
65    #[arg(
66        long = "symbol",
67        value_name = "SYMBOL",
68        required_unless_present_all = ["file", "symbol", "body"]
69    )]
70    pub symbol_flag: Option<String>,
71    /// First turn (named alternative to `<BODY>`).
72    #[arg(
73        long = "body",
74        value_name = "BODY",
75        required_unless_present_all = ["file", "symbol", "body"]
76    )]
77    pub body_flag: Option<String>,
78    /// Human-readable summary. Defaults to the first line of the first turn.
79    #[arg(long)]
80    pub title: Option<String>,
81    /// State the symbol anchor was observed against. Defaults to HEAD.
82    #[arg(long)]
83    pub state: Option<String>,
84    /// Visibility: `public` | `internal` | `team:NAME` | `restricted:LABEL` | `private:LABEL`.
85    #[arg(long)]
86    pub visibility: Option<String>,
87    /// Attach the discussion to a thread ref while keeping its symbol anchor.
88    #[arg(long, value_name = "REF")]
89    pub thread: Option<String>,
90}
91
92#[derive(Clone, Debug, Args)]
93pub struct DiscussAppendArgs {
94    pub discussion_id: String,
95    pub body: String,
96}
97
98#[derive(Clone, Debug, Args)]
99#[command(group(
100    ArgGroup::new("resolution")
101        .required(true)
102        .args(["mode", "into_annotation"])
103))]
104pub struct DiscussResolveArgs {
105    pub discussion_id: String,
106    /// Resolution kind: `by-edit` or `dismiss`.
107    #[arg(long, value_enum)]
108    pub mode: Option<ResolveModeArg>,
109    /// Resolve by creating a context annotation from this discussion.
110    #[arg(long, requires = "body")]
111    pub into_annotation: bool,
112    /// For `by-edit`: state containing the edit (defaults to HEAD).
113    #[arg(long, requires = "mode")]
114    pub state: Option<String>,
115    /// For `dismiss`: non-empty reason.
116    #[arg(long, requires = "mode")]
117    pub reason: Option<String>,
118    /// For `--into-annotation`: annotation content.
119    #[arg(long, requires = "into_annotation")]
120    pub body: Option<String>,
121    /// For `--into-annotation`: constraint, invariant, or rationale (defaults to rationale).
122    #[arg(
123        long,
124        value_parser = ["constraint", "invariant", "rationale"],
125        requires = "into_annotation"
126    )]
127    pub kind: Option<String>,
128    /// For `--into-annotation`: annotation tag (can be repeated).
129    #[arg(long, requires = "into_annotation")]
130    pub tag: Vec<String>,
131}
132
133#[derive(Clone, Debug, clap::ValueEnum)]
134pub enum ResolveModeArg {
135    ByEdit,
136    Dismiss,
137}
138
139#[derive(Clone, Debug, Args)]
140pub struct DiscussReopenArgs {
141    pub discussion_id: String,
142    /// Why the prior resolution no longer applies.
143    #[arg(long)]
144    pub reason: String,
145}
146
147#[derive(Clone, Debug, Args)]
148pub struct DiscussListArgs {
149    /// Filter by the state named in the discussion anchor.
150    #[arg(long)]
151    pub state: Option<String>,
152    /// Filter by anchored file path.
153    #[arg(long)]
154    pub file: Option<String>,
155    /// Filter by anchored symbol. Requires `--file`.
156    #[arg(long)]
157    pub symbol: Option<String>,
158    /// Status filter: `open`, `resolved`, `conflicted`, or `all`.
159    #[arg(long, default_value = "open")]
160    pub status: String,
161}
162
163#[derive(Clone, Debug, Args)]
164pub struct DiscussShowArgs {
165    pub discussion_id: String,
166}
167
168#[derive(Clone, Debug, Args)]
169pub struct DiscussWaitArgs {
170    /// Resume after this hosted event id. Defaults to the persisted watermark.
171    #[arg(long)]
172    pub after: Option<i64>,
173    /// Hosted remote that owns the event cursor. Defaults to the repository default.
174    #[arg(long)]
175    pub remote: Option<String>,
176    /// Restrict the subscription to this thread name.
177    #[arg(long)]
178    pub thread: Option<String>,
179    /// Internal helper for tests: stop after this many events (including ignored ones).
180    #[arg(long, hide = true)]
181    pub max_events: Option<usize>,
182}