Skip to main content

rocketmq_common/common/resource/
resource_pattern.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use serde::Deserialize;
16use serde::Deserializer;
17use serde::Serialize;
18use serde::Serializer;
19
20#[derive(Debug, Copy, Clone, Eq, PartialEq)]
21#[repr(u8)]
22pub enum ResourcePattern {
23    Any = 1,
24    Literal = 2,
25    Prefixed = 3,
26}
27
28impl ResourcePattern {
29    /// Case-insensitive lookup by name. Accepts either uppercase like Java or mixed case.
30    pub fn get_by_name(name: &str) -> Option<Self> {
31        if name.eq_ignore_ascii_case("ANY") {
32            Some(Self::Any)
33        } else if name.eq_ignore_ascii_case("LITERAL") {
34            Some(Self::Literal)
35        } else if name.eq_ignore_ascii_case("PREFIXED") {
36            Some(Self::Prefixed)
37        } else {
38            None
39        }
40    }
41
42    #[inline]
43    pub fn code(self) -> u8 {
44        self as u8
45    }
46
47    #[inline]
48    pub fn name(self) -> &'static str {
49        match self {
50            Self::Any => "ANY",
51            Self::Literal => "LITERAL",
52            Self::Prefixed => "PREFIXED",
53        }
54    }
55}
56
57impl Serialize for ResourcePattern {
58    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59    where
60        S: Serializer,
61    {
62        serializer.serialize_u8(self.code())
63    }
64}
65
66impl<'de> Deserialize<'de> for ResourcePattern {
67    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68    where
69        D: Deserializer<'de>,
70    {
71        let v = u8::deserialize(deserializer)?;
72        match v {
73            1 => Ok(ResourcePattern::Any),
74            2 => Ok(ResourcePattern::Literal),
75            3 => Ok(ResourcePattern::Prefixed),
76            _ => Err(serde::de::Error::custom("invalid ResourcePattern code")),
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use crate::common::resource::resource_pattern::ResourcePattern;
84
85    #[test]
86    fn given_string_any_return_enum_value() {
87        let any = ResourcePattern::get_by_name("ANY");
88        assert_eq!(Some(ResourcePattern::Any), any);
89    }
90
91    #[test]
92    fn given_string_literal_return_enum_value() {
93        let literal = ResourcePattern::get_by_name("LITERAL");
94        assert_eq!(Some(ResourcePattern::Literal), literal);
95    }
96
97    #[test]
98    fn given_string_prefixed_return_enum_value() {
99        let prefixed = ResourcePattern::get_by_name("PREFIXED");
100        assert_eq!(Some(ResourcePattern::Prefixed), prefixed);
101    }
102
103    #[test]
104    fn get_by_name_is_case_insensitive() {
105        assert_eq!(Some(ResourcePattern::Any), ResourcePattern::get_by_name("aNy"));
106        assert_eq!(Some(ResourcePattern::Literal), ResourcePattern::get_by_name("LiTeRaL"));
107        assert_eq!(
108            Some(ResourcePattern::Prefixed),
109            ResourcePattern::get_by_name("pReFixed")
110        );
111    }
112
113    #[test]
114    fn invalid_name_returns_none() {
115        assert!(ResourcePattern::get_by_name("UNKNOWN").is_none());
116        assert!(ResourcePattern::get_by_name("123").is_none());
117        assert!(ResourcePattern::get_by_name("").is_none());
118    }
119
120    #[test]
121    fn name_and_code_methods_are_consistent() {
122        assert_eq!("ANY", ResourcePattern::Any.name());
123        assert_eq!(1, ResourcePattern::Any.code());
124        assert_eq!("LITERAL", ResourcePattern::Literal.name());
125        assert_eq!(2, ResourcePattern::Literal.code());
126        assert_eq!("PREFIXED", ResourcePattern::Prefixed.name());
127        assert_eq!(3, ResourcePattern::Prefixed.code());
128    }
129
130    #[test]
131    fn serde_json_serializes_to_number() {
132        assert_eq!(
133            "1",
134            serde_json::to_string(&ResourcePattern::Any).expect("ResourcePattern::Any must serialize")
135        );
136        assert_eq!(
137            "2",
138            serde_json::to_string(&ResourcePattern::Literal).expect("ResourcePattern::Literal must serialize")
139        );
140        assert_eq!(
141            "3",
142            serde_json::to_string(&ResourcePattern::Prefixed).expect("ResourcePattern::Prefixed must serialize")
143        );
144    }
145
146    #[test]
147    fn serde_json_deserializes_from_number() {
148        let any: ResourcePattern = serde_json::from_str("1").expect("Must deserialize 1");
149        assert_eq!(ResourcePattern::Any, any);
150
151        let literal: ResourcePattern = serde_json::from_str("2").expect("Must deserialize 2");
152        assert_eq!(ResourcePattern::Literal, literal);
153
154        let prefixed: ResourcePattern = serde_json::from_str("3").expect("Must deserialize 3");
155        assert_eq!(ResourcePattern::Prefixed, prefixed);
156    }
157
158    #[test]
159    fn serde_json_deserializes_invalid_fails() {
160        assert!(serde_json::from_str::<ResourcePattern>("0").is_err());
161        assert!(serde_json::from_str::<ResourcePattern>("999").is_err());
162    }
163
164    #[test]
165    fn serde_json_roundtrip_variants() {
166        for &variant in &[
167            ResourcePattern::Any,
168            ResourcePattern::Literal,
169            ResourcePattern::Prefixed,
170        ] {
171            let serialized = serde_json::to_string(&variant)
172                .unwrap_or_else(|e| panic!("Could not serialize ResourcePattern::{:?}: {}", &variant.name(), e));
173            let parsed: ResourcePattern = serde_json::from_str(&serialized)
174                .unwrap_or_else(|e| panic!("Could not parse {:?} as ResourcePattern: {}", &serialized, e));
175            assert_eq!(variant, parsed);
176        }
177    }
178}