1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
mod central_directory;
mod central_directory_file_header;
mod data_descriptor;
mod end_of_central_directory;
mod entry;
mod error;
mod local_file_header;

pub(crate) mod prelude {
  pub(crate) use crate::error::Error;
  pub(crate) use crate::{ExpectedSize, ReadTracing, Result, WriteTracing};
  pub(crate) use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
  pub(crate) use std::io::prelude::*;
}

use prelude::*;

pub use central_directory::CentralDirectory;
pub use central_directory_file_header::CentralDirectoryFileHeader;
pub use data_descriptor::DataDescriptor;
pub use end_of_central_directory::EndOfCentralDirectory;
pub use entry::Entry;
pub use error::Error;
pub use local_file_header::LocalFileHeader;

pub type Result<T> = std::result::Result<T, Error>;

pub(crate) trait ExpectedSize {
  fn expected_size(&self) -> u32;
}

pub(crate) trait ReadTracing: Read + Seek {
  fn trace<F: FnMut(&mut Self) -> Result<T>, T: ExpectedSize>(&mut self, mut function: F) -> Result<T> {
    #[cfg(not(feature = "logging"))]
    return function(self);

    #[cfg(feature = "logging")]
    {
      let initial_stream_position = self.stream_position()?;
      log::trace!("read -> address={:#010X?}", initial_stream_position);

      let value = function(self)?;

      let final_stream_position = self.stream_position()?;
      #[cfg(feature = "discovery")]
      {
        log::trace!("read size={}, expected={}", final_stream_position - initial_stream_position, value.expected_size());
        let expected_stream_position = initial_stream_position + u64::from(value.expected_size());
        if final_stream_position != expected_stream_position {
          log::warn!("read expected to end at {:#010X}, not {:#010X}", expected_stream_position, final_stream_position);
        }
      }
      #[cfg(not(feature = "discovery"))]
      {
        log::trace!("read size={}", final_stream_position - initial_stream_position);
      }
      log::trace!("read <- address={:#010X?}", final_stream_position);

      Ok(value)
    }
  }
}

impl<T> ReadTracing for T where T: Read + Seek {}

pub(crate) trait WriteTracing: Write + Seek {
  #[cfg_attr(not(feature = "discovery"), allow(unused_variables))]
  fn trace<F: FnMut(&mut Self) -> Result<T>, T>(&mut self, expected_size: u32, mut function: F) -> Result<T> {
    #[cfg(not(feature = "logging"))]
    return function(self);

    #[cfg(feature = "logging")]
    {
      let initial_stream_position = self.stream_position()?;
      log::trace!("write -> address={:#010X?}", initial_stream_position);

      let value = function(self)?;

      let final_stream_position = self.stream_position()?;
      #[cfg(feature = "discovery")]
      {
        log::trace!("write size={}, expected={}", final_stream_position - initial_stream_position, expected_size);
        let expected_stream_position = initial_stream_position + u64::from(expected_size);
        if final_stream_position != expected_stream_position {
          log::warn!("write expected to end at {:#010X}, not {:#010X}", expected_stream_position, final_stream_position);
        }
      }
      #[cfg(not(feature = "discovery"))]
      {
        log::trace!("write size={}", final_stream_position - initial_stream_position);
      }
      log::trace!("write <- address={:#010X?}", final_stream_position);

      Ok(value)
    }
  }
}

impl<T> WriteTracing for T where T: Write + Seek {}