Skip to main content

rocketmq_security_api/
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)]
22/// Identifies the resource pattern state.
23pub enum ResourcePattern {
24    /// Represents the any case.
25    Any = 1,
26    /// Represents the literal case.
27    Literal = 2,
28    /// Represents the prefixed case.
29    Prefixed = 3,
30}
31
32impl ResourcePattern {
33    /// Case-insensitive lookup by name. Accepts either uppercase like Java or mixed case.
34    pub fn get_by_name(name: &str) -> Option<Self> {
35        if name.eq_ignore_ascii_case("ANY") {
36            Some(Self::Any)
37        } else if name.eq_ignore_ascii_case("LITERAL") {
38            Some(Self::Literal)
39        } else if name.eq_ignore_ascii_case("PREFIXED") {
40            Some(Self::Prefixed)
41        } else {
42            None
43        }
44    }
45
46    #[inline]
47    /// Returns the code.
48    pub fn code(self) -> u8 {
49        self as u8
50    }
51
52    #[inline]
53    /// Returns the name.
54    pub fn name(self) -> &'static str {
55        match self {
56            Self::Any => "ANY",
57            Self::Literal => "LITERAL",
58            Self::Prefixed => "PREFIXED",
59        }
60    }
61}
62
63impl Serialize for ResourcePattern {
64    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65    where
66        S: Serializer,
67    {
68        serializer.serialize_u8(self.code())
69    }
70}
71
72impl<'de> Deserialize<'de> for ResourcePattern {
73    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
74    where
75        D: Deserializer<'de>,
76    {
77        let v = u8::deserialize(deserializer)?;
78        match v {
79            1 => Ok(ResourcePattern::Any),
80            2 => Ok(ResourcePattern::Literal),
81            3 => Ok(ResourcePattern::Prefixed),
82            _ => Err(serde::de::Error::custom("invalid ResourcePattern code")),
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::ResourcePattern;
90
91    #[test]
92    fn given_string_any_return_enum_value() {
93        let any = ResourcePattern::get_by_name("ANY");
94        assert_eq!(Some(ResourcePattern::Any), any);
95    }
96
97    #[test]
98    fn given_string_literal_return_enum_value() {
99        let literal = ResourcePattern::get_by_name("LITERAL");
100        assert_eq!(Some(ResourcePattern::Literal), literal);
101    }
102
103    #[test]
104    fn given_string_prefixed_return_enum_value() {
105        let prefixed = ResourcePattern::get_by_name("PREFIXED");
106        assert_eq!(Some(ResourcePattern::Prefixed), prefixed);
107    }
108
109    #[test]
110    fn get_by_name_is_case_insensitive() {
111        assert_eq!(Some(ResourcePattern::Any), ResourcePattern::get_by_name("aNy"));
112        assert_eq!(Some(ResourcePattern::Literal), ResourcePattern::get_by_name("LiTeRaL"));
113        assert_eq!(
114            Some(ResourcePattern::Prefixed),
115            ResourcePattern::get_by_name("pReFixed")
116        );
117    }
118
119    #[test]
120    fn invalid_name_returns_none() {
121        assert!(ResourcePattern::get_by_name("UNKNOWN").is_none());
122        assert!(ResourcePattern::get_by_name("123").is_none());
123        assert!(ResourcePattern::get_by_name("").is_none());
124    }
125
126    #[test]
127    fn name_and_code_methods_are_consistent() {
128        assert_eq!("ANY", ResourcePattern::Any.name());
129        assert_eq!(1, ResourcePattern::Any.code());
130        assert_eq!("LITERAL", ResourcePattern::Literal.name());
131        assert_eq!(2, ResourcePattern::Literal.code());
132        assert_eq!("PREFIXED", ResourcePattern::Prefixed.name());
133        assert_eq!(3, ResourcePattern::Prefixed.code());
134    }
135
136    #[test]
137    fn serde_json_serializes_to_number() {
138        assert_eq!(
139            "1",
140            serde_json::to_string(&ResourcePattern::Any).expect("ResourcePattern::Any must serialize")
141        );
142        assert_eq!(
143            "2",
144            serde_json::to_string(&ResourcePattern::Literal).expect("ResourcePattern::Literal must serialize")
145        );
146        assert_eq!(
147            "3",
148            serde_json::to_string(&ResourcePattern::Prefixed).expect("ResourcePattern::Prefixed must serialize")
149        );
150    }
151
152    #[test]
153    fn serde_json_deserializes_from_number() {
154        let any: ResourcePattern = serde_json::from_str("1").expect("Must deserialize 1");
155        assert_eq!(ResourcePattern::Any, any);
156
157        let literal: ResourcePattern = serde_json::from_str("2").expect("Must deserialize 2");
158        assert_eq!(ResourcePattern::Literal, literal);
159
160        let prefixed: ResourcePattern = serde_json::from_str("3").expect("Must deserialize 3");
161        assert_eq!(ResourcePattern::Prefixed, prefixed);
162    }
163
164    #[test]
165    fn serde_json_deserializes_invalid_fails() {
166        assert!(serde_json::from_str::<ResourcePattern>("0").is_err());
167        assert!(serde_json::from_str::<ResourcePattern>("999").is_err());
168    }
169
170    #[test]
171    fn serde_json_roundtrip_variants() {
172        for &variant in &[
173            ResourcePattern::Any,
174            ResourcePattern::Literal,
175            ResourcePattern::Prefixed,
176        ] {
177            let serialized = serde_json::to_string(&variant)
178                .unwrap_or_else(|e| panic!("Could not serialize ResourcePattern::{:?}: {}", variant.name(), e));
179            let parsed: ResourcePattern = serde_json::from_str(&serialized)
180                .unwrap_or_else(|e| panic!("Could not parse {:?} as ResourcePattern: {}", serialized, e));
181            assert_eq!(variant, parsed);
182        }
183    }
184}