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#[derive(Debug)]
10pub struct MddFile {
11 inner: MdictFile,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct MddResource {
17 pub key: String,
18 pub data: Vec<u8>,
19}
20
21#[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#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct MddOrdinalKey {
38 pub ordinal: KeyOrdinal,
39 pub key: String,
40}
41
42pub struct MddKeyIter<'a> {
44 file: &'a MddFile,
45 block_index: usize,
46 entry_index: usize,
47 current_block: Option<Arc<Vec<DecodedKeyEntry>>>,
48}
49
50pub 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 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
61 Self::open_with_options(path, OpenOptions::default())
62 }
63
64 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 pub fn header(&self) -> &Header {
73 &self.inner.header
74 }
75
76 pub fn len(&self) -> u64 {
78 self.inner.len()
79 }
80
81 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 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 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 pub fn keys(&self) -> MddKeyIter<'_> {
122 MddKeyIter::new(self)
123 }
124
125 pub fn keys_with_ordinals(&self) -> MddOrdinalKeyIter<'_> {
127 MddOrdinalKeyIter::new(self)
128 }
129
130 pub fn key_at(&self, ordinal: KeyOrdinal) -> Result<Option<String>> {
132 self.inner.key_at_ordinal(ordinal)
133 }
134
135 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
243pub 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}