use std::path::Path;
use std::sync::Arc;
use crate::core::{DecodedKeyEntry, MdictFile};
use crate::error::{Error, Result};
use crate::types::{ContainerKind, Header, KeyOrdinal, OpenOptions};
#[derive(Debug)]
pub struct MddFile {
inner: MdictFile,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MddResource {
pub key: String,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MddResourceSpan {
pub key: String,
pub record_start: u64,
pub record_end: u64,
}
impl MddResourceSpan {
pub fn len(&self) -> u64 {
self.record_end.saturating_sub(self.record_start)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MddOrdinalKey {
pub ordinal: KeyOrdinal,
pub key: String,
}
pub struct MddKeyIter<'a> {
file: &'a MddFile,
block_index: usize,
entry_index: usize,
current_block: Option<Arc<Vec<DecodedKeyEntry>>>,
}
pub struct MddOrdinalKeyIter<'a> {
file: &'a MddFile,
block_index: usize,
entry_index: usize,
current_block: Option<Arc<Vec<DecodedKeyEntry>>>,
}
impl MddFile {
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
Self::open_with_options(path, OpenOptions::default())
}
pub fn open_with_options(path: impl AsRef<Path>, options: OpenOptions) -> Result<Self> {
Ok(Self {
inner: MdictFile::open(path, ContainerKind::Mdd, &options)?,
})
}
pub fn header(&self) -> &Header {
&self.inner.header
}
pub fn len(&self) -> u64 {
self.inner.len()
}
pub fn lookup(&self, key: &str) -> Result<Option<MddResource>> {
let Some(span) = self.lookup_span(key)? else {
return Ok(None);
};
let mut data = Vec::with_capacity(span.len() as usize);
self.read_record_span_with(&span, |chunk| {
data.extend_from_slice(chunk);
Ok(())
})?;
Ok(Some(MddResource {
key: span.key,
data,
}))
}
pub fn lookup_span(&self, key: &str) -> Result<Option<MddResourceSpan>> {
let Some((block_index, entry_index, entries)) = self.inner.find_entry(key)? else {
return Ok(None);
};
let entry = &entries[entry_index];
let record_end = self.inner.record_end(block_index, entry_index, &entries)?;
Ok(Some(MddResourceSpan {
key: entry.key.clone(),
record_start: entry.record_start,
record_end,
}))
}
pub fn read_record_span_with<F>(&self, span: &MddResourceSpan, visitor: F) -> Result<()>
where
F: FnMut(&[u8]) -> Result<()>,
{
self.inner
.visit_record_span(span.record_start, span.record_end, visitor)
}
pub fn keys(&self) -> MddKeyIter<'_> {
MddKeyIter::new(self)
}
pub fn keys_with_ordinals(&self) -> MddOrdinalKeyIter<'_> {
MddOrdinalKeyIter::new(self)
}
pub fn key_at(&self, ordinal: KeyOrdinal) -> Result<Option<String>> {
self.inner.key_at_ordinal(ordinal)
}
pub fn keys_at(&self, ordinals: &[KeyOrdinal]) -> Result<Vec<Option<String>>> {
self.inner.keys_at_ordinals(ordinals)
}
pub fn entries(&self) -> MddEntryIter<'_> {
MddEntryIter::new(self)
}
}
impl<'a> MddKeyIter<'a> {
fn new(file: &'a MddFile) -> Self {
Self {
file,
block_index: 0,
entry_index: 0,
current_block: None,
}
}
}
impl<'a> MddOrdinalKeyIter<'a> {
fn new(file: &'a MddFile) -> Self {
Self {
file,
block_index: 0,
entry_index: 0,
current_block: None,
}
}
}
impl Iterator for MddKeyIter<'_> {
type Item = Result<String>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.block_index >= self.file.inner.key_block_count() {
return None;
}
if self.current_block.is_none() {
match self.file.inner.decode_key_block(self.block_index) {
Ok(entries) => {
self.current_block = Some(entries);
self.entry_index = 0;
}
Err(error) => return Some(Err(error)),
}
}
let entries = self.current_block.as_ref().unwrap();
if self.entry_index >= entries.len() {
self.block_index += 1;
self.current_block = None;
continue;
}
let key = entries[self.entry_index].key.clone();
self.entry_index += 1;
return Some(Ok(key));
}
}
}
impl Iterator for MddOrdinalKeyIter<'_> {
type Item = Result<MddOrdinalKey>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.block_index >= self.file.inner.key_block_count() {
return None;
}
if self.current_block.is_none() {
match self.file.inner.decode_key_block(self.block_index) {
Ok(entries) => {
self.current_block = Some(entries);
self.entry_index = 0;
}
Err(error) => return Some(Err(error)),
}
}
let entries = self.current_block.as_ref().unwrap();
if self.entry_index >= entries.len() {
self.block_index += 1;
self.current_block = None;
continue;
}
let block = &self.file.inner.key_index.blocks[self.block_index];
let result = (|| -> Result<MddOrdinalKey> {
let ordinal = block
.entry_start_index
.checked_add(self.entry_index as u64)
.ok_or(Error::InvalidFormat("key ordinal overflow"))?;
Ok(MddOrdinalKey {
ordinal: KeyOrdinal::from(ordinal),
key: entries[self.entry_index].key.clone(),
})
})();
self.entry_index += 1;
return Some(result);
}
}
}
pub struct MddEntryIter<'a> {
file: &'a MddFile,
block_index: usize,
entry_index: usize,
current_block: Option<Arc<Vec<DecodedKeyEntry>>>,
}
impl<'a> MddEntryIter<'a> {
fn new(file: &'a MddFile) -> Self {
Self {
file,
block_index: 0,
entry_index: 0,
current_block: None,
}
}
}
impl Iterator for MddEntryIter<'_> {
type Item = Result<MddResource>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.block_index >= self.file.inner.key_block_count() {
return None;
}
if self.current_block.is_none() {
match self.file.inner.decode_key_block(self.block_index) {
Ok(entries) => {
self.current_block = Some(entries);
self.entry_index = 0;
}
Err(error) => return Some(Err(error)),
}
}
let entries = self.current_block.as_ref().unwrap();
if self.entry_index >= entries.len() {
self.block_index += 1;
self.current_block = None;
continue;
}
let entry = &entries[self.entry_index];
let result = (|| -> Result<MddResource> {
let record_end =
self.file
.inner
.record_end(self.block_index, self.entry_index, entries)?;
let data = self
.file
.inner
.read_record_span(entry.record_start, record_end)?;
Ok(MddResource {
key: entry.key.clone(),
data,
})
})();
self.entry_index += 1;
return Some(result);
}
}
}