picori/
ascii.rs

1//! [ASCII][`Ascii`] encoding.
2//! 
3//! [ASCII][`Ascii`] is a 7-bit encoding designed for information interchange in
4//! English. Bytes with the eighth bit set are considered invalid and will cause
5//! an [`InvalidByte`][`crate::error::DecodingProblem::InvalidByte`] to be returned.
6//! 
7
8use std::borrow::Borrow;
9use std::marker::PhantomData;
10use std::panic::Location;
11
12use crate::error::DecodingProblem::*;
13use crate::Result;
14use crate::helper::{ParseStringEncoding, ProblemLocation};
15
16/// [ASCII][`Ascii`] encoding.
17pub struct Ascii {}
18
19/// A iterator decoder for the [`Ascii`] encoding.
20pub struct Decoder<'x, I>
21where
22    I: IntoIterator,
23    I::Item: Borrow<u8> + Sized,
24{
25    iter:    <I as IntoIterator>::IntoIter,
26    _marker: PhantomData<&'x ()>,
27}
28
29impl<I> Decoder<'_, I>
30where
31    I: IntoIterator,
32    I::Item: Borrow<u8> + Sized,
33{
34    fn new<'x>(iter: I) -> Decoder<'x, I> {
35        Decoder {
36            iter:    iter.into_iter(),
37            _marker: PhantomData,
38        }
39    }
40
41    fn decode_byte(byte: u8) -> Option<char> {
42        match byte {
43            // ASCII character
44            0x00..=0x7f => Some(byte as char),
45            // Invalid
46            _ => None,
47        }
48    }
49}
50
51impl<I> Iterator for Decoder<'_, I>
52where
53    I: IntoIterator,
54    I::Item: Borrow<u8> + Sized,
55{
56    type Item = Result<char>;
57
58    fn next(&mut self) -> Option<Self::Item> {
59        if let Some(byte) = self.iter.next() {
60            let byte = byte.borrow();
61            Some(match Self::decode_byte(*byte) {
62                Some(c) => Ok(c),
63                None => Err(InvalidByte(*byte, Location::current()).into()),
64            })
65        } else {
66            None
67        }
68    }
69}
70
71impl Ascii {
72    /// Create an iterator that decodes the given iterator of bytes into
73    /// characters.
74    pub fn iter<'iter, I>(iter: I) -> Decoder<'iter, I>
75    where
76        I: IntoIterator,
77        I::Item: Borrow<u8> + Sized,
78    {
79        Decoder::new(iter)
80    }
81
82    /// Decode all bytes into a string. Will continue passed NULL bytes and only
83    /// stop at the end of the iterator or if an decoding error occurs.
84    pub fn all<I>(iter: I) -> Result<String>
85    where
86        I: IntoIterator,
87        I::Item: Borrow<u8> + Sized,
88    {
89        Self::iter(iter).collect()
90    }
91
92    /// Decode the first string (until a NULL character is reached) from the
93    /// given iterator.
94    pub fn first<I>(iter: I) -> Result<String>
95    where
96        I: IntoIterator,
97        I::Item: Borrow<u8> + Sized,
98    {
99        Self::iter(iter)
100            .take_while(|c| match c {
101                Ok(c) => *c != 0 as char,
102                Err(_) => true,
103            })
104            .collect()
105    }
106}
107
108/// Extension trait for iterators of bytes and adds the helper function
109/// [`IteratorExt::ascii`] for decoding as [ASCII][`Ascii`] strings.
110pub trait IteratorExt
111where
112    Self: IntoIterator + Sized,
113    Self::Item: Borrow<u8> + Sized,
114{
115    /// Decode self iterator of bytes as [ASCII][`Ascii`].
116    fn ascii<'b>(self) -> Decoder<'b, Self> { Decoder::new(self) }
117}
118
119impl<I> IteratorExt for I
120where
121    I: IntoIterator,
122    I::Item: Borrow<u8> + Sized,
123{
124}
125
126impl ParseStringEncoding for Ascii {
127    fn parse_str<I>(iter: I) -> Result<String>
128    where
129        I: IntoIterator,
130        I::Item: Borrow<u8> + Sized,
131    {
132        Self::first(iter)
133    }
134}
135
136// -------------------------------------------------------------------------------
137// Tests
138// -------------------------------------------------------------------------------
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn parse_str() {
146        let data = b"abc\0def";
147        assert_eq!(Ascii::parse_str(data).unwrap(), "abc".to_string());
148    }
149}