gazetta_core/
util.rs

1//  Copyright (C) 2015 Steven Allen
2//
3//  This file is part of gazetta.
4//
5//  This program is free software: you can redistribute it and/or modify it under the terms of the
6//  GNU General Public License as published by the Free Software Foundation version 3 of the
7//  License.
8//
9//  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10//  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
11//  the GNU General Public License for more details.
12//
13//  You should have received a copy of the GNU General Public License along with this program.  If
14//  not, see <http://www.gnu.org/licenses/>.
15//
16
17use std::fs::{self, File};
18use std::hash::Hasher;
19use std::io;
20use std::path::{Path, PathBuf};
21
22use crate::error::AnnotatedError;
23
24/// Recursivly copy a directory.
25///
26/// Does not preserve permissions.
27pub fn copy_recursive(src: &Path, dest: &Path) -> io::Result<()> {
28    if fs::metadata(src)?.is_dir() {
29        copy_dir(src, dest)
30    } else {
31        copy_file(src, dest)
32    }
33}
34
35fn copy_file(src: &Path, dest: &Path) -> io::Result<()> {
36    io::copy(&mut File::open(src)?, &mut File::create(dest)?).map(|_| ())
37}
38
39fn copy_dir(src: &Path, dest: &Path) -> io::Result<()> {
40    fs::create_dir(dest)?;
41    for dir_entry in fs::read_dir(src)? {
42        let dir_entry = dir_entry?;
43        let file_name = dir_entry.file_name();
44        let from = src.join(&file_name);
45        let to = dest.join(&file_name);
46        if exists(&to)? {
47            return Err(io::Error::new(
48                io::ErrorKind::AlreadyExists,
49                "target path already exists",
50            ));
51        }
52
53        if dir_entry.file_type()?.is_dir() {
54            copy_dir(&from, &to)?;
55        } else {
56            copy_file(&from, &to)?;
57        }
58    }
59    Ok(())
60}
61
62/// Check if a file exists.
63pub fn exists(path: &Path) -> io::Result<bool> {
64    match fs::metadata(path) {
65        Ok(_) => Ok(true),
66        Err(e) => match e.kind() {
67            io::ErrorKind::NotFound => Ok(false),
68            _ => Err(e),
69        },
70    }
71}
72
73/// Walk a file tree and return a sorted vector of paths.
74pub fn walk_sorted(path: &Path) -> io::Result<Vec<PathBuf>> {
75    let mut out = Vec::new();
76    walk_into(path, &mut out)?;
77    Ok(out)
78}
79
80fn walk_into(path: &Path, out: &mut Vec<PathBuf>) -> io::Result<()> {
81    let mut files = fs::read_dir(path)?
82        .map(|e| {
83            let e = e?;
84            Ok((e.path(), e.file_type()?.is_dir()))
85        })
86        .collect::<io::Result<Vec<_>>>()?;
87
88    // Don't need to sort by whole path. We're walking in sort-order.
89    files.sort_by(|a, b| a.0.file_name().unwrap().cmp(b.0.file_name().unwrap()));
90    for (path, is_dir) in files {
91        if is_dir {
92            walk_into(&path, out)?;
93        } else {
94            out.push(path);
95        }
96    }
97    Ok(())
98}
99
100pub struct StreamHasher<W, H> {
101    hash: H,
102    inner: W,
103}
104impl<W, H> StreamHasher<W, H>
105where
106    H: Hasher,
107    W: io::Write,
108{
109    pub fn new(inner: W) -> Self
110    where
111        H: Default,
112    {
113        StreamHasher {
114            hash: H::default(),
115            inner,
116        }
117    }
118    pub fn with_hasher(inner: W, hash: H) -> Self {
119        StreamHasher { hash, inner }
120    }
121    pub fn finish(&self) -> u64 {
122        self.hash.finish()
123    }
124}
125impl<W, H> io::Write for StreamHasher<W, H>
126where
127    W: io::Write,
128    H: Hasher,
129{
130    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
131        let size = self.inner.write(buf)?;
132        self.hash.write(&buf[..size]);
133        Ok(size)
134    }
135    fn flush(&mut self) -> io::Result<()> {
136        self.inner.flush()
137    }
138}
139
140/// Concatinate source paths into an output file.
141pub fn concat<W, I>(paths: I, output: &mut W) -> Result<u64, AnnotatedError<io::Error>>
142where
143    W: io::Write,
144    I: IntoIterator,
145    I::Item: AsRef<Path>,
146{
147    let mut bytes = 0;
148    for p in paths {
149        let p = p.as_ref();
150        bytes += try_annotate!(io::copy(&mut try_annotate!(File::open(p), p), output), p)
151    }
152    Ok(bytes)
153}