oi4_dnp_encoding/
validate.rs1use crate::encode::is_unreserved;
2use crate::error::{Error, ErrorKind};
3use crate::hex::{has_lowercase_hex, is_hex};
4
5#[derive(Debug, Clone, Copy, Default)]
7pub struct Rules {
8 pub enforce_reserved_masking: bool,
10 pub allow_lowercase_hex: bool,
12}
13
14impl Rules {
15 pub const fn strict_like() -> Self {
16 Self {
17 enforce_reserved_masking: true,
18 allow_lowercase_hex: false,
19 }
20 }
21}
22
23pub fn validate_dnp(input: &str, rules: &Rules) -> Result<(), Error> {
26 let bytes = input.as_bytes();
27 let mut i = 0usize;
28 while i < bytes.len() {
29 let b = bytes[i];
30 if b == b',' {
31 if i + 2 >= bytes.len() {
33 return Err(Error::new(ErrorKind::LoneComma, Some(i)));
34 }
35 let h1 = bytes[i + 1];
36 let h2 = bytes[i + 2];
37 #[cfg(feature = "strict")]
39 {
40 if has_lowercase_hex(h1, h2) {
41 return Err(Error::new(ErrorKind::LowercaseHexInStrict, Some(i)));
42 }
43 }
44
45 if !rules.allow_lowercase_hex && has_lowercase_hex(h1, h2) {
46 return Err(Error::new(ErrorKind::LowercaseHexInStrict, Some(i)));
47 }
48
49 if !is_hex(h1) {
50 return Err(Error::new(
51 ErrorKind::InvalidHexDigit(h1 as char),
52 Some(i + 1),
53 ));
54 }
55 if !is_hex(h2) {
56 return Err(Error::new(
57 ErrorKind::InvalidHexDigit(h2 as char),
58 Some(i + 2),
59 ));
60 }
61 i += 3;
62 continue;
63 }
64 if b < 0x80 {
65 if rules.enforce_reserved_masking && !is_unreserved(b) {
67 return Err(Error::new(
68 ErrorKind::UnescapedReservedAscii(b as char),
69 Some(i),
70 ));
71 }
72 i += 1;
73 } else {
74 let rest = &input[i..];
77 let ch = rest.chars().next().unwrap();
78 i += ch.len_utf8();
79 }
80 }
81 Ok(())
82}