1use chrono::NaiveDateTime;
2use failure::*;
3use libflate::zlib::Decoder;
4use std::fmt;
5use std::io::{Read, Write};
6use std::path::{Component, Path, PathBuf};
7use xmltree::Element;
8
9#[derive(Fail, Debug)]
10pub enum Errors {
11 #[fail(display = "<toc> element doesn't exist in Toc.")]
12 NoTocElement,
13 #[fail(display = "<creation-time> element doesn't exist in Toc.")]
14 NoCreationTime,
15 #[fail(display = "<checksum> element missing.")]
16 NoChecksumElement,
17 #[fail(display = "style attribute in <checksum> element missing.")]
18 NoChecksumType,
19 #[fail(display = "style attribute in <checksum> element missing.")]
20 NoFileTypeElement,
21 #[fail(display = "style attribute in <checksum> element missing.")]
22 NoFileNameElement,
23 #[fail(display = "style attribute in <checksum> element missing.")]
24 NoFileId,
25 #[fail(display = "style attribute in <checksum> element missing.")]
26 ChecksumOffsetInvalid,
27}
28
29#[derive(Debug, Clone)]
31pub struct Toc {
32 data: Element,
33}
34
35impl Toc {
36 pub fn from_read<T: Read>(reader: &mut T, _expected: usize) -> Result<Toc, Error> {
38 let mut decoder = Decoder::new(reader)?;
41 let element = Element::parse(&mut decoder)?;
42
43 Ok(Toc { data: element })
44 }
45
46 pub fn data(&self) -> &Element {
47 &self.data
48 }
49
50 pub fn write<W: Write>(&self, writer: W) -> Result<(), xmltree::Error> {
52 self.data.write(writer)
53 }
54
55 pub fn creation_time(&self) -> Result<NaiveDateTime, Error> {
57 let time = self.creation_time_element()?;
58 let text = time.text.as_ref().ok_or(Errors::NoCreationTime)?;
59 let parsed = NaiveDateTime::parse_from_str(&text, "%Y-%m-%dT%H:%M:%S")?;
60 Ok(parsed)
61 }
62
63 fn creation_time_element(&self) -> Result<&Element, Errors> {
64 self.toc_element()?
65 .get_child("creation-time")
66 .ok_or(Errors::NoCreationTime)
67 }
68
69 pub fn checksum_type(&self) -> Result<&String, Errors> {
71 self.checksum_element()?
72 .attributes
73 .get("style")
74 .ok_or(Errors::NoChecksumType)
75 }
76
77 pub fn checksum_offset(&self) -> Result<usize, Error> {
79 let re = self
80 .checksum_element()?
81 .get_child("offset")
82 .ok_or(Errors::ChecksumOffsetInvalid)?
83 .text
84 .as_ref()
85 .ok_or(Errors::ChecksumOffsetInvalid)?
86 .parse::<usize>()?;
87 Ok(re)
88 }
89
90 pub fn checksum_size(&self) -> Result<usize, Error> {
92 let re = self
93 .checksum_element()?
94 .get_child("size")
95 .ok_or(Errors::ChecksumOffsetInvalid)?
96 .text
97 .as_ref()
98 .ok_or(Errors::ChecksumOffsetInvalid)?
99 .parse::<usize>()?;
100 Ok(re)
101 }
102
103 fn checksum_element(&self) -> Result<&Element, Errors> {
104 self.toc_element()?
105 .get_child("checksum")
106 .ok_or(Errors::NoChecksumElement)
107 }
108
109 fn toc_element(&self) -> Result<&Element, Errors> {
110 self.data.get_child("toc").ok_or(Errors::NoTocElement)
111 }
112
113 pub fn files(&self) -> Result<Files, Errors> {
114 Ok(Files {
115 data: self.toc_element()?,
116 path: PathBuf::new(),
117 })
118 }
119}
120
121impl std::fmt::Display for Toc {
122 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123 write!(f, "creation-time {:?}\n", self.creation_time())?;
124 write!(f, "checksum-kind {:?}\n", self.checksum_type())?;
125 write!(f, "checksum-offset {:?}\n", self.checksum_offset())?;
126 write!(f, "checksum-size {:?}\n", self.checksum_size())
127 }
128}
129
130#[derive(Clone, Debug, Copy)]
131pub enum FileElement {
132 Data,
133 CTime,
134 MTime,
135 ATime,
136 Group,
137 GID,
138 User,
139 UID,
140 Mode,
141 INode,
142 Type,
143 Name,
144 DeviceNo,
145}
146
147impl FileElement {
148 pub fn name(&self) -> &'static str {
149 use FileElement::*;
150 match self {
151 Data => "data",
152 CTime => "ctime",
153 MTime => "mtime",
154 ATime => "atime",
155 Group => "group",
156 GID => "gid",
157 User => "user",
158 UID => "uid",
159 Mode => "mode",
160 INode => "inode",
161 Type => "type",
162 Name => "name",
163 DeviceNo => "deviceno",
164 }
165 }
166
167 pub fn from_name(name: &str) -> Option<FileElement> {
168 use FileElement::*;
169 match name {
170 "data" => Some(Data),
171 "ctime" => Some(CTime),
172 "mtime" => Some(MTime),
173 "atime" => Some(ATime),
174 "group" => Some(Group),
175 "gid" => Some(GID),
176 "user" => Some(User),
177 "uid" => Some(UID),
178 "mode" => Some(Mode),
179 "inode" => Some(INode),
180 "type" => Some(Type),
181 "name" => Some(Name),
182 "deviceno" => Some(DeviceNo),
183 _ => None,
184 }
185 }
186
187 pub fn error(&self) -> Errors {
188 use FileElement::*;
189 match self {
190 _ => Errors::NoFileTypeElement,
191 }
192 }
193}
194
195#[derive(Debug, Clone, Copy)]
196pub enum FileDataElement {
197 Length,
198 Offset,
199 Size,
200 Encoding,
201 ExtractedChecksum,
202 ArchivedChecksum,
203}
204
205impl FileDataElement {
206 pub fn name(&self) -> &'static str {
207 use FileDataElement::*;
208 match self {
209 Length => "length",
210 Offset => "offset",
211 Size => "size",
212 Encoding => "encoding",
213 ExtractedChecksum => "extracted-checksum",
214 ArchivedChecksum => "archived-checksum",
215 }
216 }
217
218 pub fn error(&self) -> Errors {
219 use FileElement::*;
220 match self {
221 _ => Errors::NoFileTypeElement,
222 }
223 }
224}
225
226#[derive(Debug, Clone, Copy)]
227pub enum FileType {
228 File,
229 Directory,
230 CharacterSpecial,
231}
232
233impl FileType {
234 pub fn from_str(name: &str) -> Option<FileType> {
235 use FileType::*;
236 match name {
237 "file" => Some(File),
238 "directory" => Some(Directory),
239 "character special" => Some(CharacterSpecial),
240 _ => None,
241 }
242 }
243}
244
245#[derive(Debug, Clone)]
246pub struct FileAttr {
247 pub name: Option<String>,
248 pub id: Option<usize>,
249 pub ftype: Option<FileType>,
250 pub user: Option<String>,
251 pub group: Option<String>,
252 pub uid: Option<usize>,
253 pub gid: Option<usize>,
254 pub deviceno: Option<usize>,
255 pub inode: Option<usize>,
256}
257
258impl FileAttr {
259 pub fn new() -> Self {
260 FileAttr {
261 name: None,
262 id: None,
263 ftype: None,
264 user: None,
265 group: None,
266 uid: None,
267 gid: None,
268 deviceno: None,
269 inode: None,
270 }
271 }
272
273 pub fn parse(data: &Element) -> FileAttr {
274 let mut attrs = FileAttr::new();
275
276 for child in &data.children {
277 let _ = attrs.parse_child(child);
278 }
279
280 attrs
281 }
282
283 fn parse_child(&mut self, child: &Element) -> Result<(), Errors> {
284 let e = FileElement::from_name(&child.name).ok_or(Errors::NoTocElement)?;
285
286 use FileElement::*;
287 match e {
288 Group => Self::parse_text(e, child, &mut self.group),
289 User => Self::parse_text(e, child, &mut self.user),
290 Name => Self::parse_text(e, child, &mut self.name),
291 Type => Self::parse_type(e, child, &mut self.ftype),
292 Data => self.parse_dummy(child),
293 CTime => self.parse_dummy(child),
294 MTime => self.parse_dummy(child),
295 ATime => self.parse_dummy(child),
296 GID => Self::parse_usize(e, child, &mut self.gid),
297 UID => Self::parse_usize(e, child, &mut self.uid),
298 Mode => self.parse_dummy(child),
299 INode => Self::parse_usize(e, child, &mut self.inode),
300 DeviceNo => Self::parse_usize(e, child, &mut self.deviceno),
301 }
302 }
303
304 fn parse_text(
305 element: FileElement,
306 child: &Element,
307 text: &mut Option<String>,
308 ) -> Result<(), Errors> {
309 *text = child.text.clone();
310 Ok(())
311 }
312
313 fn parse_type(
314 element: FileElement,
315 child: &Element,
316 ftype: &mut Option<FileType>,
317 ) -> Result<(), Errors> {
318 if let Some(text) = &child.text {
319 if let Some(nftype) = FileType::from_str(text) {
320 *ftype = Some(nftype);
321 }
322 }
323
324 Ok(())
325 }
326
327 fn parse_usize(
328 element: FileElement,
329 child: &Element,
330 out: &mut Option<usize>,
331 ) -> Result<(), Errors> {
332 let amt = child
333 .text
334 .as_ref()
335 .ok_or(element.error())?
336 .parse::<usize>()
337 .or(Err(element.error()))?;
338 *out = Some(amt);
339 Ok(())
340 }
341
342 fn parse_dummy(&mut self, child: &Element) -> Result<(), Errors> {
343 Ok(())
344 }
345}
346
347#[derive(Debug, Clone)]
349pub struct File<'a, 'b> {
350 data: &'a Element,
351 pub path: &'b Path,
352}
353
354impl<'a, 'b> File<'a, 'b> {
355 pub fn new(element: &'a Element, path: &'b Path) -> File<'a, 'b> {
356 File {
357 data: element,
358 path: path,
359 }
360 }
361
362 pub fn files(&self) -> Files<'a> {
363 let mut path = self.path.to_path_buf();
364 let attrs = self.attrs();
365 if let Some(name) = attrs.name {
367 path.push(name)
368 }
369
370 Files {
371 data: self.data,
372 path: path,
373 }
374 }
375
376 pub fn attrs(&self) -> FileAttr {
377 FileAttr::parse(&self.data)
378 }
379}
380
381#[derive(Debug, Clone)]
383pub struct Files<'a> {
384 data: &'a Element,
385 path: PathBuf,
386}
387
388impl<'a> Files<'a> {
389 pub fn iter(&self) -> FilesIter {
390 FilesIter {
391 data: self.data,
392 path: &self.path,
393 pos: 0,
394 }
395 }
396
397 pub fn find(&self, path: &Path) -> Option<Files> {
398 let mut files: Option<Files> = Some(self.clone());
399
400 files
401 }
402}
403
404#[derive(Debug, Clone)]
406pub struct FilesIter<'a, 'b> {
407 data: &'a Element,
408 path: &'b Path,
409 pos: usize,
410}
411
412impl<'a, 'b> Iterator for FilesIter<'a, 'b> {
413 type Item = File<'a, 'b>;
414 fn next(&mut self) -> Option<Self::Item> {
415 for (i, child) in self.data.children.iter().enumerate().skip(self.pos) {
416 if child.name == "file" {
417 self.pos = i + 1;
418 return Some(File {
419 data: child,
420 path: self.path,
421 });
422 }
423 }
424 None
425 }
426}