Skip to main content

systemprompt_models/notifications/
kind.rs

1//! Notification kinds: dotted lowercase names such as `document.shared`.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use std::fmt;
7use std::str::FromStr;
8
9/// A notification kind: `.`-separated segments of `[a-z0-9_]`, e.g.
10/// `document.comment.mentioned` or an extension kind `deal.stage_changed`.
11#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct NotificationKind(String);
13
14/// A string that is not a valid [`NotificationKind`].
15#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
16#[error("invalid notification kind {value:?}: {reason}")]
17pub struct NotificationKindError {
18    pub value: String,
19    pub reason: &'static str,
20}
21
22impl NotificationKind {
23    pub const MENTIONED: &'static str = "document.comment.mentioned";
24    pub const SHARED: &'static str = "document.shared";
25    pub const STREAM_PUBLISHED: &'static str = "stream.published";
26    pub const ISSUE_REPORTED: &'static str = "issue.reported";
27    pub const ISSUE_STATUS_CHANGED: &'static str = "issue.status_changed";
28
29    pub fn new(value: impl Into<String>) -> Result<Self, NotificationKindError> {
30        let value = value.into();
31        if let Some(reason) = kind_violation(&value) {
32            return Err(NotificationKindError { value, reason });
33        }
34        Ok(Self(value))
35    }
36
37    #[must_use]
38    pub const fn as_str(&self) -> &str {
39        self.0.as_str()
40    }
41}
42
43fn kind_violation(value: &str) -> Option<&'static str> {
44    if value.is_empty() {
45        return Some("must not be empty");
46    }
47    if value.split('.').any(str::is_empty) {
48        return Some("segments must not be empty");
49    }
50    if !value
51        .bytes()
52        .all(|b| b == b'.' || b == b'_' || b.is_ascii_lowercase() || b.is_ascii_digit())
53    {
54        return Some("must contain only lowercase letters, digits, '_' and '.'");
55    }
56    None
57}
58
59impl fmt::Display for NotificationKind {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.write_str(&self.0)
62    }
63}
64
65impl FromStr for NotificationKind {
66    type Err = NotificationKindError;
67
68    fn from_str(s: &str) -> Result<Self, Self::Err> {
69        Self::new(s)
70    }
71}
72
73crate::notifications::string_form::string_serde!(NotificationKind, "NotificationKind");