scolapasta_int_parse/
subject.rs1use crate::error::ArgumentError;
2
3#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
4pub struct IntegerString<'a>(&'a [u8]);
5
6impl<'a> TryFrom<&'a [u8]> for IntegerString<'a> {
7 type Error = ArgumentError<'a>;
8
9 fn try_from(subject: &'a [u8]) -> Result<Self, Self::Error> {
10 if !subject.is_ascii() {
11 return Err(subject.into());
12 }
13 if subject.contains(&b'\0') {
14 return Err(subject.into());
15 }
16 Ok(Self(subject))
17 }
18}
19
20impl<'a> TryFrom<&'a str> for IntegerString<'a> {
21 type Error = ArgumentError<'a>;
22
23 fn try_from(to_parse: &'a str) -> Result<Self, Self::Error> {
24 to_parse.as_bytes().try_into()
25 }
26}
27
28impl<'a> From<IntegerString<'a>> for &'a [u8] {
29 #[inline]
30 fn from(subject: IntegerString<'a>) -> &'a [u8] {
31 subject.as_bytes()
32 }
33}
34
35impl<'a> IntegerString<'a> {
36 #[inline]
38 #[must_use]
39 pub const fn new() -> Self {
40 Self(b"")
41 }
42
43 #[must_use]
51 pub fn from_slice(subject: &'a [u8]) -> Option<Self> {
52 subject.try_into().ok()
53 }
54
55 #[inline]
57 #[must_use]
58 pub fn as_bytes(self) -> &'a [u8] {
59 self.0
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::IntegerString;
66
67 #[test]
68 fn nul_byte_is_err() {
69 IntegerString::try_from("\0").unwrap_err();
70 IntegerString::try_from("123\0").unwrap_err();
71 IntegerString::try_from("123\x00456").unwrap_err();
72 }
73
74 #[test]
75 fn emoji_is_err() {
76 IntegerString::try_from("🕐").unwrap_err();
77 }
78
79 #[test]
80 fn invalid_utf8_is_err() {
81 IntegerString::try_from(&b"\xFF"[..]).unwrap_err();
82 }
83}