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
use alloc::vec::Vec;
use anyhow::Context;
use core::fmt::{Debug, Formatter};

use std::io::Cursor;
use std::path::PathBuf;
use std::{fs, io};

#[derive(Clone)]
/// Defines a data source, either a source `std::path::PathBuf`, or a vector of bytes.
pub enum FileDataSource {
    File(PathBuf),
    Data(Vec<u8>),
    Bytes(&'static [u8]),
}

impl Debug for FileDataSource {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match self {
            FileDataSource::File(file) => {
                f.write_fmt(format_args!("data source: File {}", file.display()))
            }
            FileDataSource::Data(d) => {
                f.write_fmt(format_args!("data source: {} raw bytes ", d.len()))
            }
            FileDataSource::Bytes(b) => {
                f.write_fmt(format_args!("data source: {} raw bytes ", b.len()))
            }
        }
    }
}

impl FileDataSource {
    /// Get the length of the inner data source
    pub fn len(&self) -> anyhow::Result<u64> {
        Ok(match self {
            FileDataSource::File(path) => fs::metadata(path)
                .with_context(|| format!("failed to read metadata of file `{}`", path.display()))?
                .len(),
            FileDataSource::Data(v) => v.len() as u64,
            FileDataSource::Bytes(s) => s.len() as u64,
        })
    }
    /// Copy this data source to the specified target that implements io::Write
    pub fn copy_to(&self, target: &mut dyn io::Write) -> anyhow::Result<()> {
        match self {
            FileDataSource::File(file_path) => {
                io::copy(
                    &mut fs::File::open(file_path).with_context(|| {
                        format!("failed to open `{}` for copying", file_path.display())
                    })?,
                    target,
                )?;
            }
            FileDataSource::Data(contents) => {
                let mut cursor = Cursor::new(contents);
                io::copy(&mut cursor, target)?;
            }
            FileDataSource::Bytes(contents) => {
                let mut cursor = Cursor::new(contents);
                io::copy(&mut cursor, target)?;
            }
        };

        Ok(())
    }
}