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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! Traits and implementations for lending iterators for hunks and metadata.
//! These APIs should be considered unstable and will be replaced with APIs
//! based around GATs and `LendingIterator` once stabilized. See [rust#44265](https://github.com/rust-lang/rust/issues/44265)
//! for the tracking issue on GAT stabilization.
//!
//! ## Iterating over hunks with `LendingIterator`
//! `LendingIterator` allows a more ergonomic interface to iterate over hunks.
//!```rust
//! use std::fs::File;
//! use std::io::BufReader;
//! use lending_iterator::LendingIterator;
//! use chd::Chd;
//!
//! let mut f = BufReader::new(File::open("file.chd")?);
//! let mut chd = Chd::open(&mut f, None)?;
//!
//! // buffer to store uncompressed hunk data must be the same length as the hunk size.
//! let mut hunk_buf = chd.get_hunksized_buffer();
//! // buffer to store compressed data.
//! let mut cmp_buf = Vec::new();
//! let mut hunks = chd.hunks();
//! while let Some(mut hunk) = hunks.next() {
//!    hunk.read_hunk_in(&mut cmp_buf, &mut hunk_buf)?;
//! }
//! ```
//!
//! ## Iterating over metadata with `LendingIterator`
//! LendingIterator allows iterating over metadata without keeping a reference to the source file.
//!```rust
//! use std::fs::File;
//! use std::io::BufReader;
//! use lending_iterator::LendingIterator;
//! use chd::Chd;
//!
//! let mut f = BufReader::new(File::open("file.chd")?);
//! let mut chd = Chd::open(&mut f, None)?;
//! let mut metadata = chd.metadata();
//! while let Some(mut metadata) = metadata.next() {
//!    let metadata = metadata.read()?;
//! }
//! ```
use crate::metadata::{Metadata, MetadataRef, MetadataRefs, MetadataTag};
use crate::Result;
use crate::{Chd, Hunk};
use lending_iterator::prelude::*;
use std::io::{Read, Seek};

#[::nougat::gat(Item)]
/// A `LendingIterator` definition re-exported from the [lending-iterator](https://crates.io/crates/lending-iterator)
/// crate. Provides an lending iterator interface with various [adapters](https://docs.rs/lending-iterator/0.1.5/lending_iterator/lending_iterator/adapters/index.html)
/// that map to those from [`Iterator`](core::iter::Iterator).
///
/// This crate defined `LendingIterator` will be replaced once a stabilized trait lands in `std`, and should
/// not be considered stable.
///
pub use lending_iterator::lending_iterator::LendingIterator;

/// An iterator over the hunks of a CHD file.
pub struct Hunks<'a, F: Read + Seek> {
    inner: &'a mut Chd<F>,
    last_hunk: u32,
    current_hunk: u32,
}

impl<'a, F: Read + Seek> Hunks<'a, F> {
    pub(crate) fn new(inner: &'a mut Chd<F>) -> Self {
        let last_hunk = inner.header().hunk_count();
        Hunks {
            inner,
            last_hunk,
            current_hunk: 0,
        }
    }
}

#[::nougat::gat]
impl<'a, F: Read + Seek> LendingIterator for Hunks<'a, F> {
    type Item<'next>
    where
        Self: 'next,
    = Hunk<'next, F>;

    fn next(&'_ mut self) -> Option<Hunk<'_, F>> {
        if self.current_hunk == self.last_hunk {
            return None;
        }
        let curr = self.current_hunk;
        self.current_hunk += 1;
        self.inner.hunk(curr).ok()
    }
}

/// An iterator over the metadata entries of a CHD file.
pub struct MetadataEntries<'a, F: Read + Seek + 'a> {
    inner: MetadataRefs<'a, F>,
}

impl<'a, F: Read + Seek + 'a> MetadataEntries<'a, F> {
    pub(crate) fn new(inner: MetadataRefs<'a, F>) -> Self {
        MetadataEntries { inner }
    }
}

impl<'a, F: Read + Seek + 'a> MetadataEntry<'a, F> {
    /// Read the contents of the metadata from the input stream.
    pub fn read(&mut self) -> Result<Metadata> {
        self.meta_ref.read(self.file)
    }
}

/// A metadata entry for a CHD file that has a reference to the source file,
/// allowing read metadata from the stream without an explicit reference.
pub struct MetadataEntry<'a, F: Read + Seek + 'a> {
    meta_ref: MetadataRef,
    file: &'a mut F,
}

impl<'a, F: Read + Seek + 'a> MetadataTag for MetadataEntry<'a, F> {
    fn metatag(&self) -> u32 {
        self.meta_ref.metatag()
    }
}

#[::nougat::gat]
impl<'a, F: Read + Seek> LendingIterator for MetadataEntries<'a, F> {
    type Item<'next>
    where
        Self: 'next,
    = MetadataEntry<'next, F>;

    fn next(&'_ mut self) -> Option<Item<'_, Self>> {
        let next = self.inner.next();
        if let Some(next) = next {
            Some(MetadataEntry {
                meta_ref: next,
                file: self.inner.file,
            })
        } else {
            None
        }
    }
}