pub(crate) mod persistence;
use std::collections::BTreeMap;
use std::io::{Read, Seek, SeekFrom};
use std::num::NonZeroU32;
use self::persistence::IndexEntry;
use crate::persistence::datablock::DataBlocksReader;
use crate::MetadataEntry;
use endiannezz::Io;
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum PageError {
#[error("I/O error: {0}.")]
Io(#[from] std::io::Error),
#[error("Invalid UTF-8 sequence.")]
UnicodeError(#[from] std::string::FromUtf8Error),
#[error("Invalid metadata: {0}")]
InvalidMetadata(String),
#[error("Failed to read a LEB128 integer: {0}.")]
Leb128Error(#[from] leb128::read::Error),
#[error("Invalid length: {0}.")]
InvalidLength(u64),
#[error("Invalid page identifier: {0}")]
InvalidId(u32),
#[error("Duplicated page identifier ({0})")]
DuplicatedId(u32),
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
pub struct PageId(NonZeroU32);
impl From<PageId> for u32 {
fn from(id: PageId) -> u32 {
id.0.get()
}
}
impl PageId {
#[cfg(test)]
pub(crate) fn force_value(id: u32) -> PageId {
PageId(NonZeroU32::new(id).unwrap())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Page {
pub(crate) id: NonZeroU32,
pub(crate) parent_id: Option<NonZeroU32>,
pub(crate) metadata: Vec<MetadataEntry>,
pub(crate) content: Vec<u8>,
}
impl Page {
pub(crate) fn new(title: String, id: NonZeroU32) -> Page {
Page {
id,
parent_id: None,
metadata: vec![MetadataEntry::Title(title)],
content: Vec::new(),
}
}
pub fn id(&self) -> PageId {
PageId(self.id)
}
pub fn set_parent(&mut self, page_id: PageId) -> &mut Page {
self.parent_id = Some(page_id.0);
self
}
pub fn add_metadata(&mut self, entry: MetadataEntry) -> &mut Page {
self.metadata.push(entry);
self
}
pub fn set_content(&mut self, content: impl Into<Vec<u8>>) -> &mut Page {
self.content = content.into();
self
}
pub fn parent(&self) -> Option<PageId> {
self.parent_id.map(PageId)
}
pub fn content(&self) -> &[u8] {
&self.content
}
pub fn metadata(&self) -> &[MetadataEntry] {
&self.metadata
}
}
pub(crate) struct Index {
entries: BTreeMap<PageId, IndexEntry>,
}
impl Index {
pub(crate) fn new<R>(mut input: R, num_pages: usize, position: u64) -> Result<Self, PageError>
where
R: Read + Seek,
{
let mut entries = BTreeMap::new();
input.seek(SeekFrom::Start(position))?;
for _ in 0..num_pages {
let ie = IndexEntry::read(&mut input)?;
let page_id = match NonZeroU32::new(ie.id) {
Some(id) => id,
None => return Err(PageError::InvalidId(ie.id)),
};
if entries.insert(PageId(page_id), ie).is_some() {
return Err(PageError::DuplicatedId(page_id.get()));
}
}
Ok(Index { entries })
}
pub(crate) fn pages_iter<'a, R>(
&'a self,
db_reader: &'a mut DataBlocksReader<R>,
) -> impl Iterator<Item = Result<Page, PageError>> + 'a
where
R: Read + Seek + 'a,
{
self.entries
.values()
.map(move |entry| persistence::build_page(entry, db_reader))
}
pub(crate) fn get_by_id<R>(
&mut self,
db_reader: &mut DataBlocksReader<R>,
page_id: PageId,
) -> Result<Page, PageError>
where
R: Read + Seek,
{
let entry = match self.entries.get(&page_id) {
Some(e) => e,
None => return Err(PageError::InvalidId(page_id.0.get())),
};
persistence::build_page(entry, db_reader)
}
}
impl<'a> IntoIterator for &'a Index {
type Item = (&'a PageId, &'a IndexEntry);
type IntoIter = std::collections::btree_map::Iter<'a, PageId, IndexEntry>;
fn into_iter(self) -> Self::IntoIter {
self.entries.iter()
}
}