Skip to main content

git_bot_feedback/comments/
thread_comments.rs

1use super::DEFAULT_MARKER;
2
3/// An enumeration of possible type of comments being posted.
4///
5/// The default is [`CommentKind::Concerns`].
6#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
7pub enum CommentKind {
8    /// A comment that admonishes concerns for end-users' attention.
9    #[default]
10    Concerns,
11
12    /// A comment that basically says "Looks Good To Me".
13    Lgtm,
14}
15
16/// An enumeration of supported behaviors about posting comments.
17///
18/// See [`ThreadCommentOptions::policy`](crate::ThreadCommentOptions::policy).
19#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
20pub enum CommentPolicy {
21    /// Each thread comment is posted as a new comment.
22    ///
23    /// This may result in perceivable spam because
24    /// every new comment may cause notification emails.
25    Anew,
26
27    /// Like [`CommentPolicy::Anew`], but updates a single comment.
28    ///
29    /// Typically, this is the desirable option when posting thread comments.
30    #[default]
31    Update,
32}
33
34/// Options that control posting comments on a thread.
35///
36/// Used as a parameter value to [`RestApiClient::post_thread_comment()`](fn@crate::client::RestApiClient::post_thread_comment).
37#[derive(Debug)]
38pub struct ThreadCommentOptions {
39    /// Controls posting comments on a thread that concerns a Pull Request or Push event.
40    ///
41    /// Typically, this is only desirable for Pull Requests.
42    pub policy: CommentPolicy,
43
44    /// The comment to post.
45    ///
46    /// This can be a blank string if [`Self::no_lgtm`] is true and the
47    /// [`Self::kind`] is [`CommentKind::Lgtm`].
48    pub comment: String,
49
50    /// The [`CommentKind`] that describes the comment's purpose.
51    pub kind: CommentKind,
52
53    /// A string used to mark/identify the thread's comment as a comment submitted by this software.
54    ///
55    /// User comments may be indistinguishable from bot/generated comments if
56    /// this value is not unique enough.
57    ///
58    /// If the git server employs Markdown syntax for comments, then
59    /// it is recommended to set this to an HTML comment that is unique to
60    /// your CI application:
61    ///
62    /// ```markdown
63    /// <!-- my-cool-CI-app-name -->
64    /// ```
65    ///
66    /// The default value for this is an HTML comment generated from
67    /// this crate's name and version along with the compile-tome's datetime.
68    /// For example:
69    ///
70    /// ```markdown
71    /// <!-- git-bot-feedback/0.1.0/Jul-14-2025_17-00 -->
72    /// ```
73    pub marker: String,
74
75    /// Disallow posting "Looks Good To Me" comments.
76    ///
77    /// Setting this option to `true` may instigate the deletion of old bot comment(s),
78    /// if any exist.
79    pub no_lgtm: bool,
80}
81
82impl Default for ThreadCommentOptions {
83    fn default() -> Self {
84        Self {
85            policy: Default::default(),
86            comment: Default::default(),
87            kind: Default::default(),
88            marker: DEFAULT_MARKER.to_string(),
89            no_lgtm: Default::default(),
90        }
91    }
92}
93
94impl ThreadCommentOptions {
95    /// Ensure that the [`ThreadCommentOptions::comment`] is marked with
96    /// the [`ThreadCommentOptions::marker`].
97    ///
98    /// Typically only used by implementations of
99    /// [`RestApiClient::post_thread_comment`](crate::client::RestApiClient::post_thread_comment)
100    /// and [`RestApiClient::append_step_summary`](crate::client::RestApiClient::append_step_summary).
101    pub fn mark_comment(&self) -> String {
102        if !self.comment.starts_with(&self.marker) {
103            return format!("{}{}", self.marker, self.comment);
104        }
105        self.comment.clone()
106    }
107}
108
109#[cfg(test)]
110mod test {
111    #![allow(clippy::unwrap_used)]
112
113    use super::{DEFAULT_MARKER, ThreadCommentOptions};
114    use chrono::NaiveDateTime;
115
116    #[test]
117    fn default_marker() {
118        let mut opts = ThreadCommentOptions::default();
119        assert_eq!(opts.marker, DEFAULT_MARKER);
120        let datetime_start = concat!(
121            "<!-- ",
122            env!("CARGO_CRATE_NAME"),
123            "/",
124            env!("CARGO_PKG_VERSION"),
125            "/",
126        )
127        .len();
128        let datetime_end = DEFAULT_MARKER.len() - 5;
129        let datetime_str = &DEFAULT_MARKER[datetime_start..datetime_end];
130        NaiveDateTime::parse_from_str(datetime_str, "%b-%d-%Y_%H-%M").unwrap();
131        assert_eq!(opts.mark_comment(), DEFAULT_MARKER);
132        let comment = format!("{DEFAULT_MARKER}Some text data.");
133        opts.comment = comment.clone();
134        assert_eq!(opts.mark_comment(), comment);
135    }
136}