radixmap/rule.rs
1//! Rule represents a match
2use super::defs::*;
3use std::str::from_utf8;
4use std::str::from_utf8_unchecked;
5
6/// An enum representing various matching patterns
7#[derive(Clone)]
8pub enum RadixRule {
9 /// Plain rule that accepts arbitrary strings
10 ///
11 /// # Syntax
12 ///
13 /// - /
14 /// - /api
15 ///
16 Plain {
17 /// fragment
18 frag: Bytes
19 },
20
21 /// Named param matches a segment of the route
22 ///
23 /// # Syntax
24 ///
25 /// - :
26 /// - :id
27 ///
28 Param {
29 /// fragment
30 frag: Bytes,
31
32 /// param's name
33 name: Bytes,
34 },
35
36 /// Unix glob style matcher, note that it must be the last component of a route
37 ///
38 /// # Syntax
39 ///
40 /// - *
41 ///
42 Glob {
43 /// fragment
44 frag: Bytes,
45
46 /// glob pattern
47 glob: glob::Pattern
48 },
49
50 /// Perl-like regular expressions
51 ///
52 /// # Syntax
53 ///
54 /// - {}
55 /// - {:}
56 /// - {\d+}
57 /// - {:\d+}
58 /// - {id:\d+}
59 ///
60 Regex {
61 /// fragment
62 frag: Bytes,
63
64 /// regex's name
65 name: Bytes,
66
67 /// the regex
68 expr: Regex,
69 },
70}
71
72impl RadixRule {
73 /// Create a plain text rule
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use radixmap::{rule::RadixRule};
79 ///
80 /// assert!(RadixRule::from_plain("").is_ok());
81 /// assert!(RadixRule::from_plain("id").is_ok());
82 /// ```
83 #[inline]
84 pub fn from_plain(frag: impl Into<Bytes>) -> RadixResult<Self> {
85 Ok(Self::Plain { frag: frag.into() })
86 }
87
88 /// Create a named param rule
89 ///
90 /// # Examples
91 ///
92 /// ```
93 /// use radixmap::{rule::RadixRule};
94 ///
95 /// assert!(RadixRule::from_param(":").is_ok()); // segment placeholder
96 /// assert!(RadixRule::from_param(":id").is_ok()); // param with a name
97 /// assert!(RadixRule::from_param("").is_err()); // missing :
98 /// assert!(RadixRule::from_param("id").is_err()); // missing :
99 /// ```
100 #[inline]
101 pub fn from_param(frag: impl Into<Bytes>) -> RadixResult<Self> {
102 let frag = frag.into();
103
104 if !frag.starts_with(b":") {
105 return Err(RadixError::PathMalformed("param lack of colon"));
106 }
107
108 let name = frag.slice(1..);
109 Ok(Self::Param { frag, name })
110 }
111
112 /// Create a unix glob style rule
113 ///
114 /// # Examples
115 ///
116 /// ```
117 /// use radixmap::{rule::RadixRule};
118 ///
119 /// assert!(RadixRule::from_glob("*").is_ok()); // match entire string
120 /// assert!(RadixRule::from_glob("*id").is_ok()); // match strings ending with 'id'
121 /// assert!(RadixRule::from_glob("").is_err()); // missing rule chars
122 /// assert!(RadixRule::from_glob("id").is_err()); // missing rule chars
123 /// ```
124 #[inline]
125 pub fn from_glob(frag: impl Into<Bytes>) -> RadixResult<Self> {
126 let frag = frag.into();
127
128 if !frag.starts_with(b"*") {
129 return Err(RadixError::PathMalformed("glob lack of asterisk"));
130 }
131
132 let glob = glob::Pattern::new(from_utf8(frag.as_ref())?)?;
133 Ok(Self::Glob { frag, glob })
134 }
135
136 /// Create a regular expression rule
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use radixmap::{rule::RadixRule};
142 ///
143 /// assert!(RadixRule::from_regex(r"{}").is_ok()); // useless but valid
144 /// assert!(RadixRule::from_regex(r"{:}").is_ok()); // same as above
145 /// assert!(RadixRule::from_regex(r"{\d+}").is_ok()); // name is empty
146 /// assert!(RadixRule::from_regex(r"{:\d+}").is_ok()); // same as above
147 /// assert!(RadixRule::from_regex(r"{id:\d+}").is_ok()); // regex with a name
148 /// assert!(RadixRule::from_regex(r"").is_err()); // missing {}
149 /// assert!(RadixRule::from_regex(r"\d+").is_err()); // missing {}
150 /// assert!(RadixRule::from_regex(r"{").is_err()); // missing }
151 /// assert!(RadixRule::from_regex(r"{[0-9}").is_err()); // missing ]
152 /// assert!(RadixRule::from_regex(r"{:(0}").is_err()); // missing )
153 /// assert!(RadixRule::from_regex(r"{id:(0}").is_err()); // missing )
154 ///
155 /// #[allow(invalid_from_utf8_unchecked)]
156 /// let invalid = format!("{{{}}}", unsafe { std::str::from_utf8_unchecked(&[0xffu8, 0xfe, 0x65]) });
157 /// assert!(RadixRule::from_regex(invalid).is_err());
158 /// ```
159 #[inline]
160 pub fn from_regex(frag: impl Into<Bytes>) -> RadixResult<Self> {
161 let frag = frag.into();
162
163 if !frag.starts_with(b"{") || !frag.ends_with(b"}") {
164 return Err(RadixError::PathMalformed("regex lack of curly braces"));
165 }
166
167 let data = frag.slice(1..frag.len() - 1);
168 let find = match memchr::memchr(b':', data.as_ref()) {
169 Some(pos) => (data.slice(..pos), from_utf8(&data[pos + 1..])?),
170 None => (Bytes::new(), from_utf8(data.as_ref())?)
171 };
172
173 // regex must match from the beginning, add ^ if needed
174 let (name, expr) = match find.1.as_bytes().first() {
175 Some(b'^') => (find.0, Regex::new(find.1)?),
176 _ => (find.0, Regex::new(('^'.to_string() + find.1).as_str())?)
177 };
178
179 Ok(Self::Regex { frag, name, expr })
180 }
181
182 /// Check if the rule is plain text
183 ///
184 /// # Examples
185 ///
186 /// ```
187 /// use radixmap::{rule::RadixRule, RadixResult};
188 ///
189 /// fn main() -> RadixResult<()> {
190 /// assert_eq!(RadixRule::from_plain("")?.is_plain(), true);
191 /// assert_eq!(RadixRule::from_param(":id")?.is_plain(), false);
192 /// assert_eq!(RadixRule::from_glob("*")?.is_plain(), false);
193 /// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?.is_plain(), false);
194 ///
195 /// Ok(())
196 /// }
197 /// ```
198 #[inline]
199 pub fn is_plain(&self) -> bool {
200 matches!(self, RadixRule::Plain { .. })
201 }
202
203 /// Check if the rule is special
204 ///
205 /// # Examples
206 ///
207 /// ```
208 /// use radixmap::{rule::RadixRule, RadixResult};
209 ///
210 /// fn main() -> RadixResult<()> {
211 /// assert_eq!(RadixRule::from_plain("")?.is_special(), false);
212 /// assert_eq!(RadixRule::from_param(":id")?.is_special(), true);
213 /// assert_eq!(RadixRule::from_glob("*")?.is_special(), true);
214 /// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?.is_special(), true);
215 ///
216 /// Ok(())
217 /// }
218 /// ```
219 #[inline]
220 pub fn is_special(&self) -> bool {
221 !self.is_plain()
222 }
223
224 /// Match the path to find the longest shared segment
225 ///
226 /// # Examples
227 ///
228 /// ```
229 /// use radixmap::{rule::RadixRule, RadixResult};
230 ///
231 /// fn main() -> RadixResult<()> {
232 /// assert_eq!(RadixRule::from_plain("")?.longest(b"", false), Some("".as_bytes()));
233 /// assert_eq!(RadixRule::from_plain("")?.longest(b"api", false), Some("".as_bytes()));
234 /// assert_eq!(RadixRule::from_plain("api")?.longest(b"api", false), Some("api".as_bytes()));
235 /// assert_eq!(RadixRule::from_plain("api/v1")?.longest(b"api", false), Some("api".as_bytes()));
236 /// assert_eq!(RadixRule::from_plain("api/v1")?.longest(b"api/v2", false), Some("api/v".as_bytes()));
237 /// assert_eq!(RadixRule::from_plain("roadmap/issues/events/6430295168")?.longest(b"roadmap/issues/events/6635165802", false), Some("roadmap/issues/events/6".as_bytes()));
238 ///
239 /// assert_eq!(RadixRule::from_param(":")?.longest(b"12345/rest", false), Some("12345".as_bytes()));
240 /// assert_eq!(RadixRule::from_param(":id")?.longest(b"12345/rest", false), Some("12345".as_bytes()));
241 /// assert_eq!(RadixRule::from_param(":id")?.longest(b"12345/rest", true), Some("".as_bytes()));
242 /// assert_eq!(RadixRule::from_param(":id")?.longest(b":id", true), Some(":id".as_bytes()));
243 ///
244 /// assert_eq!(RadixRule::from_glob("*")?.longest(b"12345/rest", false), Some("12345/rest".as_bytes()));
245 /// assert_eq!(RadixRule::from_glob("*id")?.longest(b"12345/rest", false), None);
246 /// assert_eq!(RadixRule::from_glob("*id")?.longest(b"12345/rest", true), Some("".as_bytes()));
247 /// assert_eq!(RadixRule::from_glob("*id")?.longest(b"*id", true), Some("*id".as_bytes()));
248 ///
249 /// assert_eq!(RadixRule::from_regex(r"{}")?.longest(b"12345/rest", false), Some(r"".as_bytes()));
250 /// assert_eq!(RadixRule::from_regex(r"{:}")?.longest(b"12345/rest", false), Some(r"".as_bytes()));
251 /// assert_eq!(RadixRule::from_regex(r"{\d+}")?.longest(b"12345/rest", false), Some(r"12345".as_bytes()));
252 /// assert_eq!(RadixRule::from_regex(r"{:\d+}")?.longest(b"12345/rest", false), Some(r"12345".as_bytes()));
253 /// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?.longest(b"12345/update", false), Some(r"12345".as_bytes()));
254 /// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?.longest(b"abcde", false), None);
255 /// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?.longest(b"abcde", true), Some(r"".as_bytes()));
256 /// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?.longest(br"{id:\d+}", true), Some(r"{id:\d+}".as_bytes()));
257 ///
258 /// Ok(())
259 /// }
260 /// ```
261 #[inline]
262 pub fn longest<'u>(&self, path: &'u [u8], raw: bool) -> Option<&'u [u8]> {
263 if matches!(self, RadixRule::Plain { .. }) || raw {
264 let frag = match self {
265 RadixRule::Plain { frag, .. } => frag,
266 RadixRule::Param { frag, .. } => frag,
267 RadixRule::Glob { frag, .. } => frag,
268 RadixRule::Regex { frag, .. } => frag,
269 };
270
271 // accelerating string comparison using numbers
272 let min = std::cmp::min(frag.len(), path.len());
273 let mut len = 0;
274
275 const BLK: usize = std::mem::size_of::<usize>();
276
277 while len + BLK <= min {
278 let frag_chunk: &usize = unsafe { &*(frag.as_ptr().add(len) as *const usize) };
279 let path_chunk: &usize = unsafe { &*(path.as_ptr().add(len) as *const usize) };
280
281 match frag_chunk == path_chunk {
282 true => len += BLK,
283 false => break,
284 }
285 }
286
287 // process the leftover unmatched substring
288 while len < min && frag[len] == path[len] {
289 len += 1;
290 }
291
292 return Some(&path[..len]);
293 }
294
295 match self {
296 RadixRule::Param { .. } => match memchr::memchr(b'/', path) {
297 Some(p) => Some(&path[..p]),
298 None if !path.is_empty() => Some(path),
299 None => None
300 }
301 RadixRule::Glob { glob, .. } => {
302 let utf8 = match from_utf8(path) {
303 Ok(p) => p,
304 Err(_) => return None,
305 };
306
307 match glob.matches(utf8) {
308 true => Some(path),
309 false => None
310 }
311 }
312 RadixRule::Regex { expr, .. } => {
313 let utf8 = match from_utf8(path) {
314 Ok(p) => p,
315 Err(_) => return None,
316 };
317
318 match expr.find(utf8) {
319 Some(m) => Some(&path[..m.len()]),
320 None => None
321 }
322 }
323 RadixRule::Plain { .. } => unreachable!(),
324 }
325 }
326
327 /// Divide the rule into two parts
328 ///
329 /// # Examples
330 ///
331 /// ```
332 /// use radixmap::{rule::RadixRule, RadixResult};
333 ///
334 /// fn main() -> RadixResult<()> {
335 /// let mut rule = RadixRule::from_plain("/api")?;
336 ///
337 /// assert_eq!(rule.divide(1)?, "api");
338 /// assert_eq!(rule, "/");
339 ///
340 /// assert!(RadixRule::from_param(":id")?.divide(1).is_err());
341 /// assert!(RadixRule::from_glob("*")?.divide(1).is_err());
342 /// assert!(RadixRule::from_regex(r"{id:\d+}")?.divide(1).is_err());
343 ///
344 /// Ok(())
345 /// }
346 /// ```
347 #[inline]
348 pub fn divide(&mut self, len: usize) -> RadixResult<RadixRule> {
349 match self {
350 RadixRule::Plain { frag } if frag.len() > len => {
351 let rule = RadixRule::from_plain(frag.slice(len..));
352 *frag = frag.slice(..len);
353 rule
354 }
355 _ => Err(RadixError::RuleIndivisible)
356 }
357 }
358
359 /// Origin fragment of the rule
360 ///
361 /// # Examples
362 ///
363 /// ```
364 /// use radixmap::{rule::RadixRule, RadixResult};
365 ///
366 /// fn main() -> RadixResult<()> {
367 /// assert_eq!(RadixRule::from_plain("/api")?.origin(), "/api");
368 /// assert_eq!(RadixRule::from_param(":id")?.origin(), ":id");
369 /// assert_eq!(RadixRule::from_glob("*")?.origin(), "*");
370 /// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?.origin(), r"{id:\d+}");
371 ///
372 /// Ok(())
373 /// }
374 /// ```
375 #[inline]
376 pub fn origin(&self) -> &Bytes {
377 match self {
378 RadixRule::Plain { frag } => frag,
379 RadixRule::Param { frag, .. } => frag,
380 RadixRule::Glob { frag, .. } => frag,
381 RadixRule::Regex { frag, .. } => frag,
382 }
383 }
384
385 /// The name of the named param and regex
386 ///
387 /// # Examples
388 ///
389 /// ```
390 /// use radixmap::{rule::RadixRule, RadixResult};
391 ///
392 /// fn main() -> RadixResult<()> {
393 /// assert_eq!(RadixRule::from_param(":id")?.identity(), "id");
394 /// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?.identity(), r"id");
395 ///
396 /// assert_eq!(RadixRule::from_plain("/api")?.identity(), "");
397 /// assert_eq!(RadixRule::from_param(":")?.identity(), "");
398 /// assert_eq!(RadixRule::from_glob("*")?.identity(), "*");
399 /// assert_eq!(RadixRule::from_regex(r"{\d+}")?.identity(), r"");
400 ///
401 /// Ok(())
402 /// }
403 /// ```
404 #[inline]
405 pub fn identity(&self) -> &Bytes {
406 static EMPTY: Bytes = Bytes::new();
407 static GLOB: Bytes = Bytes::from_static(b"*");
408
409 match self {
410 RadixRule::Plain { .. } => &EMPTY,
411 RadixRule::Param { name, .. } => name,
412 RadixRule::Glob { .. } => &GLOB,
413 RadixRule::Regex { name, .. } => name,
414 }
415 }
416}
417
418/// Analyze a path as long as possible and construct a rule
419///
420/// # Examples
421///
422/// ```
423/// use radixmap::{rule::RadixRule, RadixResult};
424///
425/// fn main() -> RadixResult<()> {
426/// assert!(RadixRule::try_from("").is_err());
427///
428/// assert_eq!(RadixRule::try_from("api")?, "api");
429/// assert_eq!(RadixRule::try_from("api/v1")?, "api/v1");
430/// assert_eq!(RadixRule::try_from("/api/v1")?, "/api/v1");
431///
432/// assert_eq!(RadixRule::try_from(":")?, ":");
433/// assert_eq!(RadixRule::try_from(":id")?, ":id");
434/// assert_eq!(RadixRule::try_from(":id/rest")?, ":id");
435///
436/// assert_eq!(RadixRule::try_from("*")?, "*");
437/// assert_eq!(RadixRule::try_from("*rest")?, "*rest");
438/// assert_eq!(RadixRule::try_from("*/rest")?, "*/rest");
439///
440/// assert_eq!(RadixRule::try_from(r"{id:\d+}")?, r"{id:\d+}");
441/// assert_eq!(RadixRule::try_from(r"{id:\d+}/rest")?, r"{id:\d+}");
442/// assert!(RadixRule::try_from(r"{id:\d+").is_err());
443/// assert!(RadixRule::try_from(r"{id:\d+/rest").is_err());
444///
445/// Ok(())
446/// }
447/// ```
448impl TryFrom<Bytes> for RadixRule {
449 type Error = RadixError;
450
451 fn try_from(path: Bytes) -> Result<Self, Self::Error> {
452 let init = path.first().ok_or(RadixError::PathEmpty)?;
453
454 match *init {
455 b':' => match memchr::memchr(b'/', path.as_ref()) {
456 Some(pos) => Self::from_param(path.slice(..pos)),
457 _ => Self::from_param(path),
458 }
459 b'*' => {
460 Self::from_glob(path)
461 }
462 b'{' => match memchr::memchr(b'}', path.as_ref()) {
463 Some(pos) => Self::from_regex(path.slice(..pos + 1)),
464 _ => Err(RadixError::PathMalformed("missing closing sign '}'"))
465 }
466 _ => match memchr::memchr3(b'{', b':', b'*', path.as_ref()) {
467 Some(pos) => Self::from_plain(path.slice(..pos)),
468 None => Self::from_plain(path),
469 }
470 }
471 }
472}
473
474/// Analyze a path as long as possible and construct a rule
475impl TryFrom<&'static [u8]> for RadixRule {
476 type Error = RadixError;
477
478 fn try_from(path: &'static [u8]) -> Result<Self, Self::Error> {
479 Bytes::from(path).try_into()
480 }
481}
482
483/// Analyze a path as long as possible and construct a rule
484impl TryFrom<&'static str> for RadixRule {
485 type Error = RadixError;
486
487 fn try_from(path: &'static str) -> Result<Self, Self::Error> {
488 Bytes::from(path).try_into()
489 }
490}
491
492/// Default trait
493///
494/// # Examples
495///
496/// ```
497/// use radixmap::{rule::RadixRule};
498///
499/// assert_eq!(RadixRule::default(), "");
500/// ```
501impl Default for RadixRule {
502 #[inline]
503 fn default() -> Self {
504 Self::Plain { frag: Bytes::new() }
505 }
506}
507
508/// Debug trait
509///
510/// # Examples
511///
512/// ```
513/// use radixmap::{rule::RadixRule, RadixResult};
514///
515/// fn main() -> RadixResult<()> {
516/// assert_eq!(format!("{:?}", RadixRule::from_plain("/api")?).as_str(), "Plain(/api)");
517/// assert_eq!(format!("{:?}", RadixRule::from_param(":id")?).as_str(), "Param(:id)");
518/// assert_eq!(format!("{:?}", RadixRule::from_glob("*")?).as_str(), "Glob(*)");
519/// assert_eq!(format!("{:?}", RadixRule::from_regex(r"{id:\d+}")?).as_str(), r"Regex({id:\d+})");
520///
521/// Ok(())
522/// }
523/// ```
524impl Debug for RadixRule {
525 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
526 let (kind, frag) = match self {
527 RadixRule::Plain { frag } => ("Plain", frag),
528 RadixRule::Param { frag, .. } => ("Param", frag),
529 RadixRule::Glob { frag, .. } => ("Glob", frag),
530 RadixRule::Regex { frag, .. } => ("Regex", frag),
531 };
532
533 write!(f, "{}({})", kind, unsafe { from_utf8_unchecked(frag.as_ref()) })
534 }
535}
536
537/// Hash trait
538///
539/// # Examples
540///
541/// ```
542/// use std::collections::HashMap;
543/// use radixmap::{rule::RadixRule, RadixResult};
544///
545/// fn main() -> RadixResult<()> {
546/// let mut map = HashMap::new();
547/// map.insert(RadixRule::from_plain("/api")?, "/api");
548/// map.insert(RadixRule::from_param(":id")?, ":id");
549/// map.insert(RadixRule::from_glob("*")?, "*");
550/// map.insert(RadixRule::from_regex(r"{id:\d+}")?, r"{id:\d+}");
551///
552/// assert_eq!(map[&RadixRule::from_plain("/api")?], "/api");
553/// assert_eq!(map[&RadixRule::from_param(":id")?], ":id");
554/// assert_eq!(map[&RadixRule::from_glob("*")?], "*");
555/// assert_eq!(map[&RadixRule::from_regex(r"{id:\d+}")?], r"{id:\d+}");
556///
557/// Ok(())
558/// }
559/// ```
560impl Hash for RadixRule {
561 #[inline]
562 fn hash<H: Hasher>(&self, state: &mut H) {
563 match self {
564 RadixRule::Plain { frag } => {
565 "Plain".hash(state);
566 frag.hash(state);
567 }
568 RadixRule::Param { frag, .. } => {
569 "Param".hash(state);
570 frag.hash(state);
571 }
572 RadixRule::Glob { frag, .. } => {
573 "Glob".hash(state);
574 frag.hash(state);
575 }
576 RadixRule::Regex { frag, .. } => {
577 "Regex".hash(state);
578 frag.hash(state);
579 }
580 }
581 }
582}
583
584/// == & !=
585impl Eq for RadixRule {}
586
587/// == & !=
588///
589/// # Examples
590///
591/// ```
592/// use radixmap::{rule::RadixRule, RadixResult};
593///
594/// fn main() -> RadixResult<()> {
595/// assert_eq!(RadixRule::from_plain("/api")?, RadixRule::from_plain("/api")?);
596/// assert_eq!(RadixRule::from_param(":id")?, RadixRule::from_param(":id")?);
597/// assert_eq!(RadixRule::from_glob("*")?, RadixRule::from_glob("*")?);
598/// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?, RadixRule::from_regex(r"{id:\d+}")?);
599///
600/// assert_ne!(RadixRule::from_plain("/api")?, RadixRule::from_plain("")?);
601/// assert_ne!(RadixRule::from_param(":id")?, RadixRule::from_param(":")?);
602/// assert_ne!(RadixRule::from_glob("*")?, RadixRule::from_glob("**")?);
603/// assert_ne!(RadixRule::from_regex(r"{id:\d+}")?, RadixRule::from_regex(r"{}")?);
604///
605/// // type mismatch
606/// assert_ne!(RadixRule::from_plain("{}")?, RadixRule::from_regex(r"{}")?);
607///
608/// Ok(())
609/// }
610/// ```
611impl PartialEq for RadixRule {
612 #[inline]
613 fn eq(&self, other: &Self) -> bool {
614 match (self, other) {
615 (RadixRule::Plain { frag: a }, RadixRule::Plain { frag: b }) => a == b,
616 (RadixRule::Param { frag: a, .. }, RadixRule::Param { frag: b, .. }) => a == b,
617 (RadixRule::Glob { frag: a, .. }, RadixRule::Glob { frag: b, .. }) => a == b,
618 (RadixRule::Regex { frag: a, .. }, RadixRule::Regex { frag: b, .. }) => a == b,
619 _ => false
620 }
621 }
622}
623
624/// == & !=
625///
626/// # Examples
627///
628/// ```
629/// use radixmap::{rule::RadixRule, RadixResult};
630///
631/// fn main() -> RadixResult<()> {
632/// assert_eq!(RadixRule::from_plain("/api")?, "/api");
633/// assert_eq!(RadixRule::from_param(":id")?, ":id");
634/// assert_eq!(RadixRule::from_glob("*")?, "*");
635/// assert_eq!(RadixRule::from_regex(r"{id:\d+}")?, r"{id:\d+}");
636///
637/// assert_ne!(RadixRule::from_plain("/api")?, "");
638/// assert_ne!(RadixRule::from_param(":id")?, ":");
639/// assert_ne!(RadixRule::from_glob("*")?, "**");
640/// assert_ne!(RadixRule::from_regex(r"{id:\d+}")?, r"{}");
641///
642/// Ok(())
643/// }
644/// ```
645impl PartialEq<&[u8]> for RadixRule {
646 #[inline]
647 fn eq(&self, other: &&[u8]) -> bool {
648 self.origin() == *other
649 }
650}
651
652/// == & !=
653impl<const N: usize> PartialEq<&[u8; N]> for RadixRule {
654 #[inline]
655 fn eq(&self, other: &&[u8; N]) -> bool {
656 self.origin() == other.as_ref()
657 }
658}
659
660/// == & !=
661impl PartialEq<&str> for RadixRule {
662 #[inline]
663 fn eq(&self, other: &&str) -> bool {
664 self.origin() == other.as_bytes()
665 }
666}