1use crate::parser::{
2 AppId, EncodedText, Error, Kind, MAX_APP, MAX_ID, MAX_MESSAGE, MAX_TITLE, Progress, RecordId,
3 Report, State,
4};
5
6#[derive(Debug)]
7struct Text<const N: usize> {
8 bytes: [u8; N],
9 length: usize,
10}
11
12impl<const N: usize> Text<N> {
13 const EMPTY: Self = Self {
14 bytes: [0; N],
15 length: 0,
16 };
17
18 fn copy(value: &str) -> Result<Self, Error> {
19 let mut text = Self {
20 bytes: [0; N],
21 length: value.len(),
22 };
23 text.bytes
24 .get_mut(..value.len())
25 .ok_or(Error::BufferTooSmall)?
26 .copy_from_slice(value.as_bytes());
27 Ok(text)
28 }
29
30 fn decode(&mut self, value: EncodedText<'_>) -> Result<(), Error> {
31 value.decode_into(&mut self.bytes)?;
32 self.length = value.decoded_len();
33 Ok(())
34 }
35
36 fn as_str(&self) -> Option<&str> {
37 core::str::from_utf8(&self.bytes[..self.length]).ok()
38 }
39}
40
41#[derive(Debug)]
42pub(crate) struct StoredRecord {
43 state: State,
44 id: Option<Text<MAX_ID>>,
45 app: Option<Text<MAX_APP>>,
46 title: Option<Text<MAX_TITLE>>,
47 message: Option<Text<MAX_MESSAGE>>,
48 kind: Option<Kind>,
49 progress: Option<Progress>,
50}
51
52impl StoredRecord {
53 pub(crate) fn prepare(state: State, report: Report<'_>) -> Result<Self, Error> {
54 let mut prepared = Self {
55 state,
56 id: report.id().map(|id| Text::copy(id.as_str())).transpose()?,
57 app: report
58 .app()
59 .map(|app| Text::copy(app.as_str()))
60 .transpose()?,
61 title: None,
62 message: None,
63 kind: report.kind(),
64 progress: report.progress(),
65 };
66 if let Some(title) = report.title() {
68 prepared.title.insert(Text::EMPTY).decode(title)?;
69 }
70 if let Some(message) = report.message() {
71 prepared.message.insert(Text::EMPTY).decode(message)?;
72 }
73 Ok(prepared)
74 }
75
76 pub(crate) fn matches_id(&self, id: Option<&str>) -> bool {
77 match (&self.id, id) {
78 (None, None) => true,
79 (Some(stored), Some(id)) => stored.bytes[..stored.length] == *id.as_bytes(),
80 _ => false,
81 }
82 }
83
84 pub(crate) const fn view(&self) -> Record<'_> {
85 Record { stored: self }
86 }
87
88 pub(crate) fn same_fields(&self, other: &Self) -> bool {
89 let left = self.view();
90 let right = other.view();
91 left.state() == right.state()
92 && left.id() == right.id()
93 && left.app() == right.app()
94 && left.title() == right.title()
95 && left.message() == right.message()
96 && left.kind() == right.kind()
97 && left.progress() == right.progress()
98 }
99}
100
101#[derive(Clone, Copy, Debug)]
103pub struct Record<'a> {
104 stored: &'a StoredRecord,
105}
106
107impl<'a> Record<'a> {
108 #[must_use]
110 pub const fn state(self) -> State {
111 self.stored.state
112 }
113
114 #[must_use]
116 pub fn id(self) -> Option<RecordId<'a>> {
117 RecordId::new(self.stored.id.as_ref()?.as_str()?).ok()
118 }
119
120 #[must_use]
122 pub fn app(self) -> Option<AppId<'a>> {
123 AppId::new(self.stored.app.as_ref()?.as_str()?).ok()
124 }
125
126 #[must_use]
128 pub fn title(self) -> Option<&'a str> {
129 self.stored.title.as_ref()?.as_str()
130 }
131
132 #[must_use]
134 pub fn message(self) -> Option<&'a str> {
135 self.stored.message.as_ref()?.as_str()
136 }
137
138 #[must_use]
140 pub const fn kind(self) -> Option<Kind> {
141 self.stored.kind
142 }
143
144 #[must_use]
146 pub const fn progress(self) -> Option<Progress> {
147 self.stored.progress
148 }
149}