Skip to main content

rocketmq_model/common/lite/
offset_option.rs

1// Copyright 2026 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::Serialize;
17use std::fmt;
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase")]
21#[non_exhaustive]
22pub struct OffsetOption {
23    #[serde(rename = "type")]
24    pub type_: OffsetOptionType,
25    pub value: i64,
26}
27
28impl OffsetOption {
29    pub const POLICY_LAST_VALUE: i64 = 0;
30    pub const POLICY_MIN_VALUE: i64 = 1;
31    pub const POLICY_MAX_VALUE: i64 = 2;
32
33    #[must_use]
34    #[inline]
35    pub const fn new(type_: OffsetOptionType, value: i64) -> Self {
36        Self { type_, value }
37    }
38
39    #[must_use]
40    #[inline]
41    pub const fn policy(policy: i64) -> Self {
42        debug_assert!(
43            policy == Self::POLICY_LAST_VALUE || policy == Self::POLICY_MIN_VALUE || policy == Self::POLICY_MAX_VALUE,
44            "Invalid policy value"
45        );
46        Self {
47            type_: OffsetOptionType::Policy,
48            value: policy,
49        }
50    }
51
52    #[must_use]
53    #[inline]
54    pub const fn offset(value: i64) -> Self {
55        Self {
56            type_: OffsetOptionType::Offset,
57            value,
58        }
59    }
60
61    #[must_use]
62    #[inline]
63    pub const fn tail_n(n: i64) -> Self {
64        Self {
65            type_: OffsetOptionType::TailN,
66            value: n,
67        }
68    }
69
70    #[must_use]
71    #[inline]
72    pub const fn timestamp(timestamp: i64) -> Self {
73        Self {
74            type_: OffsetOptionType::Timestamp,
75            value: timestamp,
76        }
77    }
78
79    #[inline]
80    pub const fn type_(&self) -> OffsetOptionType {
81        self.type_
82    }
83
84    #[inline]
85    pub fn set_type(&mut self, type_: OffsetOptionType) {
86        self.type_ = type_;
87    }
88
89    #[inline]
90    pub const fn value(&self) -> i64 {
91        self.value
92    }
93
94    #[inline]
95    pub fn set_value(&mut self, value: i64) {
96        self.value = value;
97    }
98}
99
100impl Default for OffsetOption {
101    fn default() -> Self {
102        Self::policy(Self::POLICY_LAST_VALUE)
103    }
104}
105
106impl fmt::Display for OffsetOption {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        write!(f, "OffsetOption {{ type: {}, value: {} }}", self.type_, self.value)
109    }
110}
111
112/// Enumeration of offset seeking strategies.
113#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
114#[non_exhaustive]
115pub enum OffsetOptionType {
116    Policy = 0,
117    Offset = 1,
118    TailN = 2,
119    Timestamp = 3,
120}
121
122impl OffsetOptionType {
123    #[must_use]
124    #[inline]
125    pub const fn from_i32(value: i32) -> Option<Self> {
126        match value {
127            0 => Some(Self::Policy),
128            1 => Some(Self::Offset),
129            2 => Some(Self::TailN),
130            3 => Some(Self::Timestamp),
131            _ => None,
132        }
133    }
134
135    #[must_use]
136    #[inline]
137    pub const fn as_i32(self) -> i32 {
138        self as i32
139    }
140}
141
142impl fmt::Display for OffsetOptionType {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        match self {
145            Self::Policy => f.write_str("POLICY"),
146            Self::Offset => f.write_str("OFFSET"),
147            Self::TailN => f.write_str("TAIL_N"),
148            Self::Timestamp => f.write_str("TIMESTAMP"),
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn offset_option_constructors() {
159        let option = OffsetOption::new(OffsetOptionType::Policy, OffsetOption::POLICY_MIN_VALUE);
160        assert_eq!(option.type_(), OffsetOptionType::Policy);
161        assert_eq!(option.value(), OffsetOption::POLICY_MIN_VALUE);
162
163        let policy = OffsetOption::policy(OffsetOption::POLICY_MAX_VALUE);
164        assert_eq!(policy.type_(), OffsetOptionType::Policy);
165        assert_eq!(policy.value(), OffsetOption::POLICY_MAX_VALUE);
166
167        let offset = OffsetOption::offset(100);
168        assert_eq!(offset.type_(), OffsetOptionType::Offset);
169        assert_eq!(offset.value(), 100);
170
171        let tail_n = OffsetOption::tail_n(10);
172        assert_eq!(tail_n.type_(), OffsetOptionType::TailN);
173        assert_eq!(tail_n.value(), 10);
174
175        let timestamp = OffsetOption::timestamp(123456789);
176        assert_eq!(timestamp.type_(), OffsetOptionType::Timestamp);
177        assert_eq!(timestamp.value(), 123456789);
178    }
179
180    #[test]
181    fn offset_option_setters() {
182        let mut option = OffsetOption::default();
183        assert_eq!(option.type_(), OffsetOptionType::Policy);
184        assert_eq!(option.value(), OffsetOption::POLICY_LAST_VALUE);
185
186        option.set_type(OffsetOptionType::TailN);
187        option.set_value(5);
188        assert_eq!(option.type_(), OffsetOptionType::TailN);
189        assert_eq!(option.value(), 5);
190    }
191
192    #[test]
193    fn offset_option_display() {
194        let option = OffsetOption::offset(100);
195        let format = format!("{}", option);
196        let expected = "OffsetOption { type: OFFSET, value: 100 }";
197        assert_eq!(format, expected);
198    }
199
200    #[test]
201    fn offset_option_serde() {
202        let option = OffsetOption::offset(100);
203        let json = serde_json::to_string(&option).unwrap();
204        let expected = r#"{"type":"Offset","value":100}"#;
205        assert_eq!(json, expected);
206        let decoded: OffsetOption = serde_json::from_str(&json).unwrap();
207        assert_eq!(option, decoded);
208    }
209
210    #[test]
211    fn offset_option_type_conversion() {
212        assert_eq!(OffsetOptionType::from_i32(0), Some(OffsetOptionType::Policy));
213        assert_eq!(OffsetOptionType::from_i32(1), Some(OffsetOptionType::Offset));
214        assert_eq!(OffsetOptionType::from_i32(2), Some(OffsetOptionType::TailN));
215        assert_eq!(OffsetOptionType::from_i32(3), Some(OffsetOptionType::Timestamp));
216        assert_eq!(OffsetOptionType::from_i32(4), None);
217
218        assert_eq!(OffsetOptionType::Policy.as_i32(), 0);
219        assert_eq!(OffsetOptionType::Offset.as_i32(), 1);
220        assert_eq!(OffsetOptionType::TailN.as_i32(), 2);
221        assert_eq!(OffsetOptionType::Timestamp.as_i32(), 3);
222    }
223
224    #[test]
225    fn offset_option_type_display() {
226        assert_eq!(format!("{}", OffsetOptionType::Policy), "POLICY");
227        assert_eq!(format!("{}", OffsetOptionType::Offset), "OFFSET");
228        assert_eq!(format!("{}", OffsetOptionType::TailN), "TAIL_N");
229        assert_eq!(format!("{}", OffsetOptionType::Timestamp), "TIMESTAMP");
230    }
231}