hff_core/write/
data_source.rs

1use crate::{Error, Result};
2use std::{
3    fmt::Debug,
4    fs::File,
5    path::{Path, PathBuf},
6};
7
8/// The source of data for given metadata or chunk.
9#[derive(Debug)]
10pub enum DataSource<'a> {
11    /// Data owned in the data source.
12    Owned(Vec<u8>),
13    /// Data referred to by the data source.
14    Ref(&'a [u8]),
15    /// An open file and the length of the data contained within it.
16    File(File, u64),
17    /// A compressed chunk data source.
18    #[cfg(feature = "compression")]
19    Compressed(u32, Option<Box<DataSource<'a>>>, Option<Vec<u8>>),
20}
21
22impl<'a> DataSource<'a> {
23    /// Create a new owned data source.
24    pub fn owned(data: impl Into<Vec<u8>>) -> Self {
25        Self::Owned(data.into())
26    }
27
28    /// Create a data source referencing a u8 array.
29    pub fn reference(data: &'a [u8]) -> Self {
30        Self::Ref(data)
31    }
32
33    /// Create a new file data source.
34    pub fn file(source: File, len: u64) -> Self {
35        Self::File(source, len)
36    }
37
38    /// Create a new compressed data source.
39    #[cfg(feature = "compression")]
40    pub fn compressed(level: u32, source: DataSource<'a>) -> Self {
41        Self::Compressed(level, Some(Box::new(source)), None)
42    }
43
44    /// Get the length of the content if known at this time.
45    pub fn len(&self) -> Option<usize> {
46        match self {
47            Self::Owned(d) => Some(d.len()),
48            Self::Ref(d) => Some(d.len()),
49            Self::File(_, l) => Some(*l as usize),
50            #[cfg(feature = "compression")]
51            Self::Compressed(_, _, data) => {
52                if let Some(data) = data {
53                    Some(data.len())
54                } else {
55                    None
56                }
57            }
58        }
59    }
60
61    /// Prepare the content of the data.
62    /// For compressed content this runs the default compression
63    /// and turns this into an Owned variant.
64    pub fn prepare(&mut self) -> Result<u64> {
65        match self {
66            #[cfg(feature = "compression")]
67            Self::Compressed(level, source, data) => {
68                // Take the source item and collapse it into the owned data entry
69                // if needed.  (Prepare is re-entrant and could be called several
70                // times.)
71                if let Some(source) = source.take() {
72                    let source = *source;
73                    match source {
74                        DataSource::Owned(d) => *data = Some(d),
75                        DataSource::Ref(d) => *data = Some(d.into()),
76                        DataSource::File(mut f, _) => {
77                            let mut buffer = vec![];
78                            std::io::copy(&mut f, &mut buffer)?;
79                            *data = Some(buffer);
80                        }
81                        _ => panic!(
82                            "Invalid data source.  Compressing compressed data for instance."
83                        ),
84                    }
85                } else {
86                    // Already prepared or internal error.
87                    if let Some(data) = data {
88                        return Ok(data.len() as u64);
89                    } else {
90                        return Err(Error::Invalid(
91                            "Internal error dealing with compressed data.".into(),
92                        ));
93                    }
94                };
95
96                // Compress it and replace data.
97                let mut encoder = xz2::write::XzEncoder::new(vec![], (*level).min(9));
98                let source = data.take().unwrap();
99                std::io::copy(&mut source.as_slice(), &mut encoder)?;
100                *data = Some(encoder.finish()?);
101
102                Ok(data.as_ref().unwrap().len() as u64)
103            }
104            // Everything else can be used as is.
105            _ => Ok(self.len().unwrap() as u64),
106        }
107    }
108}
109
110impl<'a> TryInto<DataSource<'a>> for &str {
111    type Error = Error;
112
113    fn try_into(self) -> std::prelude::v1::Result<DataSource<'a>, Self::Error> {
114        Ok(DataSource::Owned(self.as_bytes().into()))
115    }
116}
117
118impl<'a> TryInto<DataSource<'a>> for String {
119    type Error = Error;
120
121    fn try_into(self) -> std::prelude::v1::Result<DataSource<'a>, Self::Error> {
122        Ok(DataSource::Owned(self.as_bytes().into()))
123    }
124}
125
126impl<'a> TryInto<DataSource<'a>> for &[u8] {
127    type Error = Error;
128
129    fn try_into(self) -> std::prelude::v1::Result<DataSource<'a>, Self::Error> {
130        Ok(DataSource::Owned(self.into()))
131    }
132}
133
134impl<'a> TryInto<DataSource<'a>> for Vec<u8> {
135    type Error = Error;
136
137    fn try_into(self) -> std::prelude::v1::Result<DataSource<'a>, Self::Error> {
138        Ok(DataSource::Owned(self))
139    }
140}
141
142impl<'a> TryInto<DataSource<'a>> for &Path {
143    type Error = Error;
144
145    fn try_into(self) -> std::prelude::v1::Result<DataSource<'a>, Self::Error> {
146        let file = std::fs::File::open(self)?;
147        let length = file.metadata()?.len();
148        Ok(DataSource::File(file, length))
149    }
150}
151
152impl<'a> TryInto<DataSource<'a>> for PathBuf {
153    type Error = Error;
154
155    fn try_into(self) -> std::prelude::v1::Result<DataSource<'a>, Self::Error> {
156        let file = std::fs::File::open(&self)?;
157        let length = file.metadata()?.len();
158        Ok(DataSource::File(file, length))
159    }
160}
161
162#[cfg(feature = "compression")]
163impl<'a, T> TryInto<DataSource<'a>> for (u32, T)
164where
165    T: TryInto<DataSource<'a>>,
166    <T as TryInto<DataSource<'a>>>::Error: std::fmt::Debug,
167    Error: From<<T as TryInto<DataSource<'a>>>::Error>,
168{
169    type Error = Error;
170
171    fn try_into(self) -> std::prelude::v1::Result<DataSource<'a>, Self::Error> {
172        Ok(DataSource::Compressed(
173            self.0,
174            Some(Box::new(self.1.try_into()?)),
175            None,
176        ))
177    }
178}