Skip to main content

mdict_rs/
mdd.rs

1use std::path::Path;
2use std::sync::Arc;
3
4use crate::core::{DecodedKeyEntry, MdictFile};
5use crate::error::{Error, Result};
6use crate::types::{ContainerKind, Header, KeyOrdinal, OpenOptions};
7
8/// Opened `.mdd` resource dictionary file.
9#[derive(Debug)]
10pub struct MddFile {
11    inner: MdictFile,
12}
13
14/// A decoded MDD key/resource pair.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct MddResource {
17    pub key: String,
18    pub data: Vec<u8>,
19}
20
21/// A lazily readable MDD record span.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct MddResourceSpan {
24    pub key: String,
25    pub record_start: u64,
26    pub record_end: u64,
27}
28
29impl MddResourceSpan {
30    pub fn len(&self) -> u64 {
31        self.record_end.saturating_sub(self.record_start)
32    }
33}
34
35/// A decoded MDD key with its stable ordinal.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct MddOrdinalKey {
38    pub ordinal: KeyOrdinal,
39    pub key: String,
40}
41
42/// A lazy iterator over decoded MDD keys without reading resource payloads.
43pub struct MddKeyIter<'a> {
44    file: &'a MddFile,
45    block_index: usize,
46    entry_index: usize,
47    current_block: Option<Arc<Vec<DecodedKeyEntry>>>,
48}
49
50/// A lazy iterator over decoded MDD keys with stable ordinals.
51pub struct MddOrdinalKeyIter<'a> {
52    file: &'a MddFile,
53    block_index: usize,
54    entry_index: usize,
55    current_block: Option<Arc<Vec<DecodedKeyEntry>>>,
56}
57
58impl MddFile {
59    /// Opens an MDD file with default options.
60    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
61        Self::open_with_options(path, OpenOptions::default())
62    }
63
64    /// Opens an MDD file with explicit options.
65    pub fn open_with_options(path: impl AsRef<Path>, options: OpenOptions) -> Result<Self> {
66        Ok(Self {
67            inner: MdictFile::open(path, ContainerKind::Mdd, &options)?,
68        })
69    }
70
71    /// Returns the parsed top-level header metadata.
72    pub fn header(&self) -> &Header {
73        &self.inner.header
74    }
75
76    /// Returns the number of resource entries declared by the file.
77    pub fn len(&self) -> u64 {
78        self.inner.len()
79    }
80
81    /// Performs a lazy lookup for a single resource key.
82    pub fn lookup(&self, key: &str) -> Result<Option<MddResource>> {
83        let Some(span) = self.lookup_span(key)? else {
84            return Ok(None);
85        };
86        let mut data = Vec::with_capacity(span.len() as usize);
87        self.read_record_span_with(&span, |chunk| {
88            data.extend_from_slice(chunk);
89            Ok(())
90        })?;
91        Ok(Some(MddResource {
92            key: span.key,
93            data,
94        }))
95    }
96
97    /// Resolves a single resource key into a lazily readable record span.
98    pub fn lookup_span(&self, key: &str) -> Result<Option<MddResourceSpan>> {
99        let Some((block_index, entry_index, entries)) = self.inner.find_entry(key)? else {
100            return Ok(None);
101        };
102        let entry = &entries[entry_index];
103        let record_end = self.inner.record_end(block_index, entry_index, &entries)?;
104        Ok(Some(MddResourceSpan {
105            key: entry.key.clone(),
106            record_start: entry.record_start,
107            record_end,
108        }))
109    }
110
111    /// Reads a resolved resource span chunk by chunk.
112    pub fn read_record_span_with<F>(&self, span: &MddResourceSpan, visitor: F) -> Result<()>
113    where
114        F: FnMut(&[u8]) -> Result<()>,
115    {
116        self.inner
117            .visit_record_span(span.record_start, span.record_end, visitor)
118    }
119
120    /// Returns a lazy iterator over decoded keys without reading resources.
121    pub fn keys(&self) -> MddKeyIter<'_> {
122        MddKeyIter::new(self)
123    }
124
125    /// Returns a lazy iterator over decoded keys with stable ordinals.
126    pub fn keys_with_ordinals(&self) -> MddOrdinalKeyIter<'_> {
127        MddOrdinalKeyIter::new(self)
128    }
129
130    /// Returns the original key at a stable ordinal without reading resources.
131    pub fn key_at(&self, ordinal: KeyOrdinal) -> Result<Option<String>> {
132        self.inner.key_at_ordinal(ordinal)
133    }
134
135    /// Returns original keys for a batch of ordinals in input order.
136    pub fn keys_at(&self, ordinals: &[KeyOrdinal]) -> Result<Vec<Option<String>>> {
137        self.inner.keys_at_ordinals(ordinals)
138    }
139
140    pub fn entries(&self) -> MddEntryIter<'_> {
141        MddEntryIter::new(self)
142    }
143}
144
145impl<'a> MddKeyIter<'a> {
146    fn new(file: &'a MddFile) -> Self {
147        Self {
148            file,
149            block_index: 0,
150            entry_index: 0,
151            current_block: None,
152        }
153    }
154}
155
156impl<'a> MddOrdinalKeyIter<'a> {
157    fn new(file: &'a MddFile) -> Self {
158        Self {
159            file,
160            block_index: 0,
161            entry_index: 0,
162            current_block: None,
163        }
164    }
165}
166
167impl Iterator for MddKeyIter<'_> {
168    type Item = Result<String>;
169
170    fn next(&mut self) -> Option<Self::Item> {
171        loop {
172            if self.block_index >= self.file.inner.key_block_count() {
173                return None;
174            }
175
176            if self.current_block.is_none() {
177                match self.file.inner.decode_key_block(self.block_index) {
178                    Ok(entries) => {
179                        self.current_block = Some(entries);
180                        self.entry_index = 0;
181                    }
182                    Err(error) => return Some(Err(error)),
183                }
184            }
185
186            let entries = self.current_block.as_ref().unwrap();
187            if self.entry_index >= entries.len() {
188                self.block_index += 1;
189                self.current_block = None;
190                continue;
191            }
192
193            let key = entries[self.entry_index].key.clone();
194            self.entry_index += 1;
195            return Some(Ok(key));
196        }
197    }
198}
199
200impl Iterator for MddOrdinalKeyIter<'_> {
201    type Item = Result<MddOrdinalKey>;
202
203    fn next(&mut self) -> Option<Self::Item> {
204        loop {
205            if self.block_index >= self.file.inner.key_block_count() {
206                return None;
207            }
208
209            if self.current_block.is_none() {
210                match self.file.inner.decode_key_block(self.block_index) {
211                    Ok(entries) => {
212                        self.current_block = Some(entries);
213                        self.entry_index = 0;
214                    }
215                    Err(error) => return Some(Err(error)),
216                }
217            }
218
219            let entries = self.current_block.as_ref().unwrap();
220            if self.entry_index >= entries.len() {
221                self.block_index += 1;
222                self.current_block = None;
223                continue;
224            }
225
226            let block = &self.file.inner.key_index.blocks[self.block_index];
227            let result = (|| -> Result<MddOrdinalKey> {
228                let ordinal = block
229                    .entry_start_index
230                    .checked_add(self.entry_index as u64)
231                    .ok_or(Error::InvalidFormat("key ordinal overflow"))?;
232                Ok(MddOrdinalKey {
233                    ordinal: KeyOrdinal::from(ordinal),
234                    key: entries[self.entry_index].key.clone(),
235                })
236            })();
237            self.entry_index += 1;
238            return Some(result);
239        }
240    }
241}
242
243/// Lazy iterator over decoded MDD resources.
244pub struct MddEntryIter<'a> {
245    file: &'a MddFile,
246    block_index: usize,
247    entry_index: usize,
248    current_block: Option<Arc<Vec<DecodedKeyEntry>>>,
249}
250
251impl<'a> MddEntryIter<'a> {
252    fn new(file: &'a MddFile) -> Self {
253        Self {
254            file,
255            block_index: 0,
256            entry_index: 0,
257            current_block: None,
258        }
259    }
260}
261
262impl Iterator for MddEntryIter<'_> {
263    type Item = Result<MddResource>;
264
265    fn next(&mut self) -> Option<Self::Item> {
266        loop {
267            if self.block_index >= self.file.inner.key_block_count() {
268                return None;
269            }
270
271            if self.current_block.is_none() {
272                match self.file.inner.decode_key_block(self.block_index) {
273                    Ok(entries) => {
274                        self.current_block = Some(entries);
275                        self.entry_index = 0;
276                    }
277                    Err(error) => return Some(Err(error)),
278                }
279            }
280
281            let entries = self.current_block.as_ref().unwrap();
282            if self.entry_index >= entries.len() {
283                self.block_index += 1;
284                self.current_block = None;
285                continue;
286            }
287
288            let entry = &entries[self.entry_index];
289            let result = (|| -> Result<MddResource> {
290                let record_end =
291                    self.file
292                        .inner
293                        .record_end(self.block_index, self.entry_index, entries)?;
294                let data = self
295                    .file
296                    .inner
297                    .read_record_span(entry.record_start, record_end)?;
298                Ok(MddResource {
299                    key: entry.key.clone(),
300                    data,
301                })
302            })();
303            self.entry_index += 1;
304            return Some(result);
305        }
306    }
307}