Skip to main content

rocketmq_client_rust/hook/
check_forbidden_context.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 std::fmt;
16
17use crate::ClientError;
18use cheetah_string::CheetahString;
19use rocketmq_model::common::message::message_queue::MessageQueue;
20use rocketmq_model::common::message::MessageTrait;
21
22use crate::implementation::communication_mode::CommunicationMode;
23use crate::producer::send_result::SendResult;
24
25/// Context information for forbidden operation checks.
26///
27/// Contains all relevant information about a message send operation,
28/// allowing hooks to inspect and validate the operation before execution.
29///
30/// # Examples
31///
32/// ```ignore
33/// use rocketmq_client::CheckForbiddenContext;
34/// use cheetah_string::CheetahString;
35///
36/// let mut context = CheckForbiddenContext::default();
37/// context.group = Some(CheetahString::from("test_group"));
38/// context.broker_addr = Some(CheetahString::from("127.0.0.1:10911"));
39/// context.unit_mode = false;
40/// ```
41#[derive(Default)]
42pub struct CheckForbiddenContext<'a> {
43    /// Name server address
44    pub name_srv_addr: Option<CheetahString>,
45    /// Producer or consumer group name
46    pub group: Option<CheetahString>,
47    /// Message being sent (borrowed reference)
48    pub message: Option<&'a dyn MessageTrait>,
49    /// Target message queue (borrowed reference)
50    pub mq: Option<&'a MessageQueue>,
51    /// Broker address
52    pub broker_addr: Option<CheetahString>,
53    /// Communication mode (sync, async, or oneway)
54    pub communication_mode: Option<CommunicationMode>,
55    /// Send result (available after sending)
56    pub send_result: Option<SendResult>,
57    /// Exception that occurred during sending
58    pub exception: Option<ClientError>,
59    /// Custom argument
60    pub arg: Option<Box<dyn std::any::Any + Send + Sync>>,
61    /// Whether unit mode is enabled
62    pub unit_mode: bool,
63}
64
65impl<'a> CheckForbiddenContext<'a> {
66    /// Creates a new context.
67    #[inline]
68    pub fn new() -> Self {
69        Self::default()
70    }
71
72    /// Sets the name server address.
73    #[inline]
74    pub fn with_name_srv_addr(mut self, addr: impl Into<CheetahString>) -> Self {
75        self.name_srv_addr = Some(addr.into());
76        self
77    }
78
79    /// Sets the group name.
80    #[inline]
81    pub fn with_group(mut self, group: impl Into<CheetahString>) -> Self {
82        self.group = Some(group.into());
83        self
84    }
85
86    /// Sets the broker address.
87    #[inline]
88    pub fn with_broker_addr(mut self, addr: impl Into<CheetahString>) -> Self {
89        self.broker_addr = Some(addr.into());
90        self
91    }
92
93    /// Sets the communication mode.
94    #[inline]
95    pub fn with_communication_mode(mut self, mode: CommunicationMode) -> Self {
96        self.communication_mode = Some(mode);
97        self
98    }
99
100    /// Sets the unit mode flag.
101    #[inline]
102    pub fn with_unit_mode(mut self, unit_mode: bool) -> Self {
103        self.unit_mode = unit_mode;
104        self
105    }
106}
107
108impl<'a> fmt::Display for CheckForbiddenContext<'a> {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(f, "CheckForbiddenContext {{ ")?;
111
112        if let Some(ref addr) = self.name_srv_addr {
113            write!(f, "name_srv_addr: {}, ", addr)?;
114        }
115
116        if let Some(ref group) = self.group {
117            write!(f, "group: {}, ", group)?;
118        }
119
120        if self.message.is_some() {
121            write!(f, "message: Some(_), ")?;
122        }
123
124        if let Some(ref mq) = self.mq {
125            write!(f, "mq: {}, ", mq)?;
126        }
127
128        if let Some(ref addr) = self.broker_addr {
129            write!(f, "broker_addr: {}, ", addr)?;
130        }
131
132        if let Some(ref mode) = self.communication_mode {
133            write!(f, "communication_mode: {}, ", mode)?;
134        }
135
136        if self.send_result.is_some() {
137            write!(f, "send_result: Some(_), ")?;
138        }
139
140        if self.exception.is_some() {
141            write!(f, "exception: Some(_), ")?;
142        }
143
144        write!(f, "unit_mode: {} }}", self.unit_mode)
145    }
146}
147
148impl<'a> fmt::Debug for CheckForbiddenContext<'a> {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        f.debug_struct("CheckForbiddenContext")
151            .field("name_srv_addr", &self.name_srv_addr)
152            .field("group", &self.group)
153            .field("message", &self.message.as_ref().map(|_| "Some(_)"))
154            .field("mq", &self.mq)
155            .field("broker_addr", &self.broker_addr)
156            .field("communication_mode", &self.communication_mode)
157            .field("send_result", &self.send_result.as_ref().map(|_| "Some(_)"))
158            .field("exception", &self.exception.as_ref().map(|_| "Some(_)"))
159            .field("arg", &self.arg.as_ref().map(|_| "Some(_)"))
160            .field("unit_mode", &self.unit_mode)
161            .finish()
162    }
163}
164#[cfg(test)]
165mod tests {
166    use super::*;
167
168    #[test]
169    fn test_new_equals_default() {
170        let ctx1 = CheckForbiddenContext::default();
171        let ctx2 = CheckForbiddenContext::new();
172
173        assert_eq!(ctx1.name_srv_addr, ctx2.name_srv_addr);
174        assert_eq!(ctx1.group, ctx2.group);
175        assert_eq!(ctx1.broker_addr, ctx2.broker_addr);
176        assert_eq!(ctx1.communication_mode, ctx2.communication_mode);
177        assert_eq!(ctx1.unit_mode, ctx2.unit_mode);
178    }
179
180    #[test]
181    fn test_builder_sets_single_fields() {
182        let ctx = CheckForbiddenContext::new()
183            .with_name_srv_addr("127.0.0.1:9876")
184            .with_group("test_group")
185            .with_broker_addr("127.0.0.1:10911")
186            .with_unit_mode(true);
187
188        assert_eq!(ctx.name_srv_addr.unwrap().as_str(), "127.0.0.1:9876");
189        assert_eq!(ctx.group.unwrap().as_str(), "test_group");
190        assert_eq!(ctx.broker_addr.unwrap().as_str(), "127.0.0.1:10911");
191        assert!(ctx.unit_mode);
192    }
193
194    #[test]
195    fn test_builder_does_not_override_previous_fields() {
196        let ctx = CheckForbiddenContext::new()
197            .with_group("group_a")
198            .with_name_srv_addr("ns_addr")
199            .with_group("group_b"); // override group intentionally
200
201        assert_eq!(ctx.group.unwrap().as_str(), "group_b");
202        assert_eq!(ctx.name_srv_addr.unwrap().as_str(), "ns_addr");
203    }
204
205    #[test]
206    fn test_communication_mode_assignment() {
207        let ctx = CheckForbiddenContext::new().with_communication_mode(CommunicationMode::Sync);
208
209        assert!(matches!(ctx.communication_mode, Some(CommunicationMode::Sync)));
210    }
211
212    #[test]
213    fn display_uses_java_communication_mode_name() {
214        let ctx = CheckForbiddenContext::new().with_communication_mode(CommunicationMode::Oneway);
215
216        let rendered = ctx.to_string();
217
218        assert!(rendered.contains("communication_mode: ONEWAY"));
219        assert!(!rendered.contains("communication_mode: Oneway"));
220    }
221
222    #[test]
223    fn test_unit_mode_default_is_false() {
224        let ctx = CheckForbiddenContext::new();
225        assert!(!ctx.unit_mode);
226    }
227
228    #[test]
229    fn test_custom_arg_downcast() {
230        let mut ctx = CheckForbiddenContext::new();
231        ctx.arg = Some(Box::new(100u32));
232
233        let value = ctx.arg.as_ref().unwrap().downcast_ref::<u32>().unwrap();
234
235        assert_eq!(*value, 100);
236    }
237}