1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use crate::{check_len, Error, Result, TryRead, TryWrite};
use core::str;

/// Context for &str to determine where a &str ends.
///
/// Pattern will **not** be included in the result
///
/// Default to `NULL` delimiter.
///
/// # Example
///
/// ```
/// use byte::*;
/// use byte::ctx::*;
///
/// let bytes: &[u8] = b"hello, world!\0";
///
/// let str: &str = bytes.read(&mut 0).unwrap();
/// assert_eq!(str, "hello, world!");
///
/// let str: &str = bytes.read_with(&mut 0, Str::Len(5)).unwrap();
/// assert_eq!(str, "hello");
///
/// let str: &str = bytes.read_with(&mut 0, Str::Delimiter(b"!"[0])).unwrap();
/// assert_eq!(str, "hello, world");
///
/// let str: &str = bytes.read_with(&mut 0, Str::DelimiterUntil(NULL, 5)).unwrap();
/// assert_eq!(str, "hello");
/// ```
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Str {
    /// Take fix-length bytes as str
    Len(usize),
    /// Take bytes until reaching a delimiter
    Delimiter(u8),
    /// Take bytes until either delimiter or length reached
    DelimiterUntil(u8, usize),
}

impl Default for Str {
    #[inline]
    fn default() -> Self {
        Str::Delimiter(NULL)
    }
}

/// Null string delimiter
pub const NULL: u8 = 0;
/// Space string delimiter
pub const SPACE: u8 = 0x20;
/// Return string delimiter
pub const RET: u8 = 0x0a;
/// Tab string delimiter
pub const TAB: u8 = 0x09;

impl<'a> TryRead<'a, Str> for &'a str {
    #[inline]
    fn try_read(bytes: &'a [u8], ctx: Str) -> Result<(Self, usize)> {
        let (bytes, size) = match ctx {
            Str::Len(len) => {
                let len = check_len(bytes, len)?;
                (&bytes[..len], len)
            }
            Str::Delimiter(delimiter) => {
                let position = bytes
                    .iter()
                    .position(|c| *c == delimiter)
                    .ok_or(Error::Incomplete)?;
                (&bytes[..position], position + 1)
            }
            Str::DelimiterUntil(delimiter, len) => {
                let position = bytes.iter().take(len).position(|c| *c == delimiter);
                match position {
                    Some(position) => (&bytes[..position], position + 1),
                    None => {
                        let len = check_len(bytes, len)?;
                        (&bytes[..len], len)
                    }
                }
            }
        };

        match str::from_utf8(bytes) {
            Ok(str) => Ok((str, size)),
            Err(_) => Err(Error::BadInput { err: "UTF8 Error" }),
        }
    }
}

impl<'a> TryWrite for &'a str {
    #[inline]
    fn try_write(self, bytes: &mut [u8], _ctx: ()) -> Result<usize> {
        let str_bytes = self.as_bytes();

        check_len(bytes, str_bytes.len())?;

        bytes[..str_bytes.len()].clone_from_slice(str_bytes);

        Ok(str_bytes.len())
    }
}