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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use internal::{self, DirEntry, consts};
use std::path::{Path, PathBuf};
use std::time::SystemTime;

// ========================================================================= //

/// Metadata about a single object (storage or stream) in a compound file.
#[derive(Clone)]
pub struct Entry {
    name: String,
    path: PathBuf,
    obj_type: u8,
    clsid: [u8; 16],
    state_bits: u32,
    creation_time: u64,
    modified_time: u64,
    stream_len: u64,
}

pub fn new_entry(dir_entry: &DirEntry, path: PathBuf) -> Entry {
    Entry {
        name: dir_entry.name.clone(),
        path: path,
        obj_type: dir_entry.obj_type,
        clsid: dir_entry.clsid,
        state_bits: dir_entry.state_bits,
        creation_time: dir_entry.creation_time,
        modified_time: dir_entry.modified_time,
        stream_len: dir_entry.stream_len,
    }
}

impl Entry {
    /// Returns the name of the object that this entry represents.
    pub fn name(&self) -> &str { &self.name }

    /// Returns the full path to the object that this entry represents.
    pub fn path(&self) -> &Path { &self.path }

    /// Returns whether this entry is for a stream object (i.e. a "file" within
    /// the compound file).
    pub fn is_stream(&self) -> bool {
        self.obj_type == consts::OBJ_TYPE_STREAM
    }

    /// Returns whether this entry is for a storage object (i.e. a "directory"
    /// within the compound file), either the root or a nested storage.
    pub fn is_storage(&self) -> bool {
        self.obj_type == consts::OBJ_TYPE_STORAGE ||
        self.obj_type == consts::OBJ_TYPE_ROOT
    }

    /// Returns whether this entry is specifically for the root storage object
    /// of the compound file.
    pub fn is_root(&self) -> bool { self.obj_type == consts::OBJ_TYPE_ROOT }

    /// Returns the size, in bytes, of the stream that this metadata is for.
    pub fn len(&self) -> u64 { self.stream_len }

    /// Returns the CLSID (that is, the object class GUID) for this object.
    /// This will always be all zeros for stream objects.
    pub fn clsid(&self) -> &[u8; 16] { &self.clsid }

    /// Returns the user-defined bitflags set for this object.
    pub fn state_bits(&self) -> u32 { self.state_bits }

    /// Returns the time when the object that this entry represents was
    /// created.
    pub fn created(&self) -> SystemTime {
        internal::time::system_time_from_timestamp(self.creation_time)
    }

    /// Returns the time when the object that this entry represents was last
    /// modified.
    pub fn modified(&self) -> SystemTime {
        internal::time::system_time_from_timestamp(self.modified_time)
    }
}

// ========================================================================= //

/// An iterator over the entries in a storage object.
pub struct Entries<'a> {
    directory: &'a Vec<DirEntry>,
    path: PathBuf,
    stack: Vec<u32>,
    current: u32,
}

pub fn new_entries<'a>(directory: &'a Vec<DirEntry>, path: PathBuf,
                       start: u32)
                       -> Entries<'a> {
    Entries {
        directory: directory,
        path: path,
        stack: Vec::new(),
        current: start,
    }
}

impl<'a> Iterator for Entries<'a> {
    type Item = Entry;

    fn next(&mut self) -> Option<Entry> {
        while self.current != consts::NO_STREAM {
            self.stack.push(self.current);
            self.current = self.directory[self.current as usize].left_sibling;
        }
        if let Some(parent) = self.stack.pop() {
            let dir_entry = &self.directory[parent as usize];
            self.current = dir_entry.right_sibling;
            Some(new_entry(dir_entry, self.path.join(&dir_entry.name)))
        } else {
            None
        }
    }
}

// ========================================================================= //