Skip to main content

gn_core/
namespace.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4/// The three built-in note namespaces. Extensible via `Custom`.
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub enum Namespace {
7    Comments,
8    Review,
9    Todos,
10    Custom(String),
11}
12
13impl Namespace {
14    /// Returns the full ref path for this namespace.
15    pub fn ref_path(&self) -> String {
16        match self {
17            Namespace::Comments => "refs/notes/comments".to_string(),
18            Namespace::Review => "refs/notes/review".to_string(),
19            Namespace::Todos => "refs/notes/todos".to_string(),
20            Namespace::Custom(name) => format!("refs/notes/{}", name),
21        }
22    }
23
24    /// Parses a string into a Namespace. Will match built-in names if possible.
25    #[allow(clippy::should_implement_trait)]
26    pub fn from_str(s: &str) -> Self {
27        // Strip the standard prefix if provided
28        let stripped = s.strip_prefix("refs/notes/").unwrap_or(s);
29        match stripped {
30            "comments" => Namespace::Comments,
31            "review" => Namespace::Review,
32            "todos" => Namespace::Todos,
33            other => Namespace::Custom(other.to_string()),
34        }
35    }
36}
37
38impl fmt::Display for Namespace {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            Namespace::Comments => write!(f, "comments"),
42            Namespace::Review => write!(f, "review"),
43            Namespace::Todos => write!(f, "todos"),
44            Namespace::Custom(name) => write!(f, "{}", name),
45        }
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn test_from_str_builtin_without_prefix() {
55        assert_eq!(Namespace::from_str("comments"), Namespace::Comments);
56        assert_eq!(Namespace::from_str("review"), Namespace::Review);
57        assert_eq!(Namespace::from_str("todos"), Namespace::Todos);
58    }
59
60    #[test]
61    fn test_from_str_builtin_with_prefix() {
62        assert_eq!(Namespace::from_str("refs/notes/comments"), Namespace::Comments);
63        assert_eq!(Namespace::from_str("refs/notes/review"), Namespace::Review);
64        assert_eq!(Namespace::from_str("refs/notes/todos"), Namespace::Todos);
65    }
66
67    #[test]
68    fn test_from_str_custom() {
69        assert_eq!(
70            Namespace::from_str("bugs"),
71            Namespace::Custom("bugs".to_string())
72        );
73        assert_eq!(
74            Namespace::from_str("refs/notes/bugs"),
75            Namespace::Custom("bugs".to_string())
76        );
77    }
78
79    #[test]
80    fn test_ref_path_and_display() {
81        let cases = vec![
82            (Namespace::Comments, "refs/notes/comments", "comments"),
83            (Namespace::Review, "refs/notes/review", "review"),
84            (Namespace::Todos, "refs/notes/todos", "todos"),
85            (Namespace::Custom("my-notes".to_string()), "refs/notes/my-notes", "my-notes"),
86        ];
87
88        for (ns, expected_ref, expected_display) in cases {
89            assert_eq!(ns.ref_path(), expected_ref);
90            assert_eq!(ns.to_string(), expected_display);
91            assert_eq!(Namespace::from_str(expected_ref), ns);
92            assert_eq!(Namespace::from_str(expected_display), ns);
93        }
94    }
95}