1use crate::AprsData;
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum DuplicateDecision {
11 New,
13 Duplicate,
15}
16
17impl DuplicateDecision {
18 #[must_use]
20 pub const fn code(self) -> &'static str {
21 match self {
22 Self::New => "duplicate.new",
23 Self::Duplicate => "duplicate.duplicate",
24 }
25 }
26}
27
28#[derive(Clone, Debug, Eq, PartialEq)]
30pub struct DuplicateWindow {
31 capacity: usize,
32 packets: Vec<Vec<u8>>,
33}
34
35impl DuplicateWindow {
36 #[must_use]
39 pub const fn new(capacity: usize) -> Self {
40 Self {
41 capacity,
42 packets: Vec::new(),
43 }
44 }
45
46 pub fn observe(&mut self, packet: &[u8]) -> DuplicateDecision {
49 if self.packets.iter().any(|existing| existing == packet) {
50 return DuplicateDecision::Duplicate;
51 }
52
53 if self.capacity > 0 {
54 if self.packets.len() == self.capacity {
55 self.packets.remove(0);
56 }
57 self.packets.push(packet.to_vec());
58 }
59
60 DuplicateDecision::New
61 }
62
63 #[must_use]
65 pub fn retained_len(&self) -> usize {
66 self.packets.len()
67 }
68
69 #[must_use]
71 pub const fn capacity(&self) -> usize {
72 self.capacity
73 }
74}
75
76#[derive(Clone, Copy, Debug, Eq, PartialEq)]
78pub enum RateLimitDecision {
79 Allowed,
81 Limited,
83}
84
85impl RateLimitDecision {
86 #[must_use]
88 pub const fn code(self) -> &'static str {
89 match self {
90 Self::Allowed => "rate.allowed",
91 Self::Limited => "rate.limited",
92 }
93 }
94}
95
96#[derive(Clone, Copy, Debug, Eq, PartialEq)]
98pub struct PacketRateBudget {
99 limit: u64,
100 remaining: u64,
101}
102
103impl PacketRateBudget {
104 #[must_use]
107 pub const fn new(limit: u64) -> Self {
108 Self {
109 limit,
110 remaining: limit,
111 }
112 }
113
114 pub fn try_consume(&mut self) -> RateLimitDecision {
116 if self.remaining == 0 {
117 return RateLimitDecision::Limited;
118 }
119
120 self.remaining -= 1;
121 RateLimitDecision::Allowed
122 }
123
124 pub fn reset(&mut self) {
126 self.remaining = self.limit;
127 }
128
129 #[must_use]
131 pub const fn limit(&self) -> u64 {
132 self.limit
133 }
134
135 #[must_use]
137 pub const fn remaining(&self) -> u64 {
138 self.remaining
139 }
140}
141
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
144pub enum SemanticFamily {
145 Status,
147 Position,
149 TimestampedPosition,
151 CompressedPosition,
153 Message,
155 Object,
157 Item,
159 Weather,
161 Telemetry,
163 TelemetryMetadata,
165 Query,
167 Capability,
169 Nmea,
171 MicE,
173 Maidenhead,
175 UserDefined,
177 ThirdParty,
179 Unsupported,
181 Malformed,
183}
184
185impl SemanticFamily {
186 #[must_use]
188 pub const fn from_aprs_data(data: &AprsData<'_>) -> Self {
189 match data {
190 AprsData::Status { .. } => Self::Status,
191 AprsData::Position(_) => Self::Position,
192 AprsData::TimestampedPosition(_) => Self::TimestampedPosition,
193 AprsData::CompressedPosition(_) => Self::CompressedPosition,
194 AprsData::Message(_) => Self::Message,
195 AprsData::Object(_) => Self::Object,
196 AprsData::Item(_) => Self::Item,
197 AprsData::Weather(_) => Self::Weather,
198 AprsData::Telemetry(_) => Self::Telemetry,
199 AprsData::TelemetryMetadata(_) => Self::TelemetryMetadata,
200 AprsData::Query(_) => Self::Query,
201 AprsData::Capability(_) => Self::Capability,
202 AprsData::Nmea(_) => Self::Nmea,
203 AprsData::MicE(_) => Self::MicE,
204 AprsData::Maidenhead(_) => Self::Maidenhead,
205 AprsData::UserDefined(_) => Self::UserDefined,
206 AprsData::ThirdParty(_) => Self::ThirdParty,
207 AprsData::Unsupported { .. } => Self::Unsupported,
208 AprsData::Malformed { .. } => Self::Malformed,
209 }
210 }
211
212 #[must_use]
214 pub const fn code(self) -> &'static str {
215 match self {
216 Self::Status => "status",
217 Self::Position => "position",
218 Self::TimestampedPosition => "timestamped_position",
219 Self::CompressedPosition => "compressed_position",
220 Self::Message => "message",
221 Self::Object => "object",
222 Self::Item => "item",
223 Self::Weather => "weather",
224 Self::Telemetry => "telemetry",
225 Self::TelemetryMetadata => "telemetry_metadata",
226 Self::Query => "query",
227 Self::Capability => "capability",
228 Self::Nmea => "nmea",
229 Self::MicE => "mic_e",
230 Self::Maidenhead => "maidenhead",
231 Self::UserDefined => "user_defined",
232 Self::ThirdParty => "third_party",
233 Self::Unsupported => "unsupported",
234 Self::Malformed => "malformed",
235 }
236 }
237}
238
239#[derive(Clone, Copy, Debug, Eq, PartialEq)]
241pub struct SemanticBlocklist<'a> {
242 families: &'a [SemanticFamily],
243}
244
245impl<'a> SemanticBlocklist<'a> {
246 #[must_use]
248 pub const fn new(families: &'a [SemanticFamily]) -> Self {
249 Self { families }
250 }
251
252 #[must_use]
254 pub fn rejects(&self, data: &AprsData<'_>) -> bool {
255 let family = SemanticFamily::from_aprs_data(data);
256 self.families.contains(&family)
257 }
258
259 #[must_use]
261 pub const fn families(&self) -> &'a [SemanticFamily] {
262 self.families
263 }
264}