1use crate::error::Error;
14use alloc::vec::Vec;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18#[repr(u8)]
19#[allow(missing_docs)]
20pub enum TextCommand {
21 PermanentNoWrap = 0x01,
22 PermanentWrap = 0x02,
23 TemporaryNoWrap = 0x03,
24 TemporaryWrap = 0x04,
25}
26
27impl TextCommand {
28 pub const fn from_byte(b: u8) -> Result<Self, Error> {
30 Ok(match b {
31 0x01 => Self::PermanentNoWrap,
32 0x02 => Self::PermanentWrap,
33 0x03 => Self::TemporaryNoWrap,
34 0x04 => Self::TemporaryWrap,
35 _ => {
36 return Err(Error::MalformedPayload {
37 code: 0x6B,
38 reason: "unknown TEXT command code",
39 });
40 }
41 })
42 }
43
44 pub const fn as_byte(self) -> u8 {
46 self as u8
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct Text {
53 pub reader: u8,
55 pub command: TextCommand,
57 pub temp_time_s: u8,
59 pub row: u8,
61 pub column: u8,
63 pub text: Vec<u8>,
65}
66
67impl Text {
68 fn validate_ascii(s: &[u8]) -> Result<(), Error> {
70 for &b in s {
71 if !(0x20..=0x7E).contains(&b) {
72 return Err(Error::MalformedPayload {
73 code: 0x6B,
74 reason: "TEXT must be printable ASCII",
75 });
76 }
77 }
78 Ok(())
79 }
80
81 pub fn encode(&self) -> Result<Vec<u8>, Error> {
83 Self::validate_ascii(&self.text)?;
84 if self.text.len() > u8::MAX as usize {
85 return Err(Error::MalformedPayload {
86 code: 0x6B,
87 reason: "TEXT length exceeds 255 bytes",
88 });
89 }
90 let mut out = Vec::with_capacity(6 + self.text.len());
91 out.push(self.reader);
92 out.push(self.command.as_byte());
93 out.push(self.temp_time_s);
94 out.push(self.row);
95 out.push(self.column);
96 out.push(self.text.len() as u8);
97 out.extend_from_slice(&self.text);
98 Ok(out)
99 }
100
101 pub fn decode(data: &[u8]) -> Result<Self, Error> {
103 if data.len() < 6 {
104 return Err(Error::MalformedPayload {
105 code: 0x6B,
106 reason: "TEXT requires at least 6 bytes",
107 });
108 }
109 let length = data[5] as usize;
110 if data.len() != 6 + length {
111 return Err(Error::MalformedPayload {
112 code: 0x6B,
113 reason: "TEXT length field disagrees with payload",
114 });
115 }
116 let text = data[6..6 + length].to_vec();
117 Self::validate_ascii(&text)?;
118 Ok(Self {
119 reader: data[0],
120 command: TextCommand::from_byte(data[1])?,
121 temp_time_s: data[2],
122 row: data[3],
123 column: data[4],
124 text,
125 })
126 }
127}