main
rs 99 lines 2.55 KB
Raw
1 //! Issues. Served by the MCP tools; see [`crate::mcp`] for why.
2
3 use {
4 crate::{
5 client::Client,
6 error::Result,
7 models::{CreatedRef, Issue, IssueSummary},
8 },
9 serde_json::json,
10 std::fmt,
11 };
12
13 /// Which issues or pull requests to list.
14 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15 pub enum State {
16 #[default]
17 Open,
18 Closed,
19 Merged,
20 All,
21 }
22
23 impl State {
24 pub fn as_str(&self) -> &'static str {
25 match self {
26 State::Open => "open",
27 State::Closed => "closed",
28 State::Merged => "merged",
29 State::All => "all",
30 }
31 }
32 }
33
34 impl fmt::Display for State {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 f.write_str(self.as_str())
37 }
38 }
39
40 impl Client {
41 /// Issues in `owner/repo`, optionally filtered by a title/body substring.
42 pub async fn issues(
43 &self,
44 repo: &str,
45 state: State,
46 query: Option<&str>,
47 ) -> Result<Vec<IssueSummary>> {
48 let mut args = json!({ "repo": repo, "state": state.as_str() });
49 if let Some(query) = query {
50 args["query"] = query.into();
51 }
52 self.call_tool_as("list_issues", args).await
53 }
54
55 /// One issue with its comments.
56 pub async fn issue(&self, repo: &str, number: i64) -> Result<Issue> {
57 self.call_tool_as("get_issue", json!({ "repo": repo, "number": number }))
58 .await
59 }
60
61 /// Open a new issue.
62 pub async fn create_issue(
63 &self,
64 repo: &str,
65 title: &str,
66 body: Option<&str>,
67 ) -> Result<CreatedRef> {
68 let mut args = json!({ "repo": repo, "title": title });
69 if let Some(body) = body {
70 args["body"] = body.into();
71 }
72 self.call_tool_as("create_issue", args).await
73 }
74
75 /// Comment on an issue *or* a pull request — they share one number space.
76 pub async fn add_comment(&self, repo: &str, number: i64, body: &str) -> Result<CommentRef> {
77 let args = json!({ "repo": repo, "number": number, "body": body });
78 self.call_tool_as("add_issue_comment", args).await
79 }
80 }
81
82 /// What `add_issue_comment` returns.
83 #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
84 pub struct CommentRef {
85 pub id: i64,
86 pub url: String,
87 }
88
89 #[cfg(test)]
90 mod tests {
91 use super::*;
92
93 #[test]
94 fn states_use_the_wire_spelling_the_server_expects() {
95 assert_eq!(State::Open.as_str(), "open");
96 assert_eq!(State::Merged.as_str(), "merged");
97 assert_eq!(State::default(), State::Open);
98 }
99 }