use crate::parser::{
AppId, EncodedText, Error, Kind, MAX_APP, MAX_ID, MAX_MESSAGE, MAX_TITLE, Progress, RecordId,
Report, State,
};
#[derive(Debug)]
struct Text<const N: usize> {
bytes: [u8; N],
length: usize,
}
impl<const N: usize> Text<N> {
const EMPTY: Self = Self {
bytes: [0; N],
length: 0,
};
fn copy(value: &str) -> Result<Self, Error> {
let mut text = Self {
bytes: [0; N],
length: value.len(),
};
text.bytes
.get_mut(..value.len())
.ok_or(Error::BufferTooSmall)?
.copy_from_slice(value.as_bytes());
Ok(text)
}
fn decode(&mut self, value: EncodedText<'_>) -> Result<(), Error> {
value.decode_into(&mut self.bytes)?;
self.length = value.decoded_len();
Ok(())
}
fn as_str(&self) -> Option<&str> {
core::str::from_utf8(&self.bytes[..self.length]).ok()
}
}
#[derive(Debug)]
pub(crate) struct StoredRecord {
state: State,
id: Option<Text<MAX_ID>>,
app: Option<Text<MAX_APP>>,
title: Option<Text<MAX_TITLE>>,
message: Option<Text<MAX_MESSAGE>>,
kind: Option<Kind>,
progress: Option<Progress>,
}
impl StoredRecord {
pub(crate) fn prepare(state: State, report: Report<'_>) -> Result<Self, Error> {
let mut prepared = Self {
state,
id: report.id().map(|id| Text::copy(id.as_str())).transpose()?,
app: report
.app()
.map(|app| Text::copy(app.as_str()))
.transpose()?,
title: None,
message: None,
kind: report.kind(),
progress: report.progress(),
};
if let Some(title) = report.title() {
prepared.title.insert(Text::EMPTY).decode(title)?;
}
if let Some(message) = report.message() {
prepared.message.insert(Text::EMPTY).decode(message)?;
}
Ok(prepared)
}
pub(crate) fn matches_id(&self, id: Option<&str>) -> bool {
match (&self.id, id) {
(None, None) => true,
(Some(stored), Some(id)) => stored.bytes[..stored.length] == *id.as_bytes(),
_ => false,
}
}
pub(crate) const fn view(&self) -> Record<'_> {
Record { stored: self }
}
pub(crate) fn same_fields(&self, other: &Self) -> bool {
let left = self.view();
let right = other.view();
left.state() == right.state()
&& left.id() == right.id()
&& left.app() == right.app()
&& left.title() == right.title()
&& left.message() == right.message()
&& left.kind() == right.kind()
&& left.progress() == right.progress()
}
}
#[derive(Clone, Copy, Debug)]
pub struct Record<'a> {
stored: &'a StoredRecord,
}
impl<'a> Record<'a> {
#[must_use]
pub const fn state(self) -> State {
self.stored.state
}
#[must_use]
pub fn id(self) -> Option<RecordId<'a>> {
RecordId::new(self.stored.id.as_ref()?.as_str()?).ok()
}
#[must_use]
pub fn app(self) -> Option<AppId<'a>> {
AppId::new(self.stored.app.as_ref()?.as_str()?).ok()
}
#[must_use]
pub fn title(self) -> Option<&'a str> {
self.stored.title.as_ref()?.as_str()
}
#[must_use]
pub fn message(self) -> Option<&'a str> {
self.stored.message.as_ref()?.as_str()
}
#[must_use]
pub const fn kind(self) -> Option<Kind> {
self.stored.kind
}
#[must_use]
pub const fn progress(self) -> Option<Progress> {
self.stored.progress
}
}