commit 146792b65fc0e4a209c7bf32a79b69469c3d793c Author: Avril Date: Sun Apr 26 05:03:54 2020 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02d1bca --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +target/ +output/ +*~ +#*# diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7ab9f49 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,440 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "async-std" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" +dependencies = [ + "async-task", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "futures-core", + "futures-io", + "futures-timer", + "kv-log-macro", + "log", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "async-task" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" +dependencies = [ + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "crossbeam-channel" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" + +[[package]] +name = "futures-io" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" + +[[package]] +name = "futures-timer" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "hashlink" +version = "0.1.0" +dependencies = [ + "async-std", + "sha2", +] + +[[package]] +name = "hermit-abi" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "memoffset" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mio" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-uds" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "pin-project-lite" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sha2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dc3603a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "hashlink" +version = "0.1.0" +authors = ["Avril "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sha2 = "0.8.1" +async-std = "1.3.0" \ No newline at end of file diff --git a/src/hash.rs b/src/hash.rs new file mode 100644 index 0000000..7c4fc0c --- /dev/null +++ b/src/hash.rs @@ -0,0 +1,135 @@ +extern crate sha2; +extern crate async_std; +use sha2::{Sha256, Digest}; +use async_std::io::ReadExt; + +pub struct FileHash([u8; 32]); + +fn copy_slice(src: &[T], dst: &mut [T]) -> usize { + let mut c =0; + for (d, s) in dst.iter_mut().zip(src.iter()) { + *d = *s; + c += 1; + } + c +} + +const BLOCK_SIZE: usize = 32; +async fn copy_to_hash(src: &mut async_std::fs::File, mut do_block: F) -> Result +{ + assert!(BLOCK_SIZE>0); + + let mut res: usize =0; + let mut buf: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; + + loop { + let n = src.read(&mut buf).await?; + if n == 0 { + break; + } + do_block(&buf[..n]); + res+=n; + } + Ok(res) +} + +impl FileHash { + pub fn create(file: &mut std::fs::File) -> Result { + let mut hash = Sha256::default(); + std::io::copy(file, &mut hash)?; + + let mut u: [u8; 32] = [0; 32]; + copy_slice(&hash.result()[..], &mut u); + Ok(Self(u)) + } + + pub async fn create_async(file: &mut async_std::fs::File) -> Result { + let mut hash = Sha256::default(); + //async_std::io::copy(file, &mut hash).await?; + + copy_to_hash(file, |buffer| { + &hash.input(buffer); + }).await?; + + let mut u: [u8; 32] = [0; 32]; + copy_slice(&hash.result()[..], &mut u); + + Ok(Self(u)) + } + + pub fn iter(&self) -> std::slice::Iter + { + self.0.iter() + } + + pub fn iter_mut(&mut self) -> std::slice::IterMut + { + self.0.iter_mut() + } +} + +impl Clone for FileHash { + fn clone(&self) -> Self { + let mut u: [u8; 32] = [0; 32]; + copy_slice(&self.0, &mut u); + Self(u) + } +} + +impl AsRef<[u8; 32]> for FileHash { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl AsMut<[u8; 32]> for FileHash { + fn as_mut(&mut self) -> &mut [u8; 32] { + &mut self.0 + } +} + +impl Default for FileHash { + fn default() -> Self { + Self([0; 32]) + } +} + +fn hex_string(bytes: &[u8]) -> String { + bytes.iter().map(|x| format!("{:02x}", x)).collect::() +} + +impl std::fmt::Display for FileHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex_string(&self.0)) + } +} + +impl std::cmp::PartialEq for FileHash { + fn eq(&self, other: &Self) -> bool { + self.0.iter() + .zip(other.0 + .iter()) + .map(|(x, y)| x^y) + .fold(0, |x, y| x|y) == 0 + } +} +impl std::cmp::Eq for FileHash{} + +impl<'a> IntoIterator for &'a mut FileHash { + type Item = &'a mut u8; + type IntoIter = std::slice::IterMut<'a, u8>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl<'a> IntoIterator for &'a FileHash { + type Item = &'a u8; + type IntoIter = std::slice::Iter<'a, u8>; + + fn into_iter(self) -> Self::IntoIter + { + self.0.iter() + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..683e80e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,101 @@ +#![allow(dead_code)] +#![allow(unused_imports)] + +extern crate sha2; + +use std::fs::{File}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::io::prelude::*; +use sha2::{Sha256}; + +mod hash; +mod traverse; +mod pool; +mod rotating_list; + +fn funcall(w: T) +{ + w(); +} + +const THREADS: usize = 3; +const OUTPUT_DIR: &'static str = "./output"; +const INPUT_DIR: &'static str = "./target"; + +macro_rules! join_path { + ($($path:expr),*) => { + { + let mut buf = std::path::PathBuf::new(); + $( + buf.push($path); + )+ + buf + } + } +} + +fn output_path(path: &Path) -> Result { + let mut buf = PathBuf::new(); + buf.push(OUTPUT_DIR); + if let Some(path) = path.to_str() { + buf.push(&path[INPUT_DIR.len()+1..]); + Ok(buf) + } else { + Err("Bad pathspec") + } +} + +fn main() { + let root = traverse::DirectoryIterator::begin(INPUT_DIR).unwrap(); + + let mut pools = create_pools!(THREADS); + + if !Path::new(OUTPUT_DIR).exists() { + if let Err(err) = fs::create_dir(OUTPUT_DIR) { + println!("Could not create output directory: {}", err); + return; + } + } + + let hardlink_dir = join_path!(OUTPUT_DIR, ".hashlink"); + + if !hardlink_dir.exists() { + if let Err(err) = fs::create_dir(hardlink_dir.as_path()) { + println!("Could not create .hashlink directory: {}", err); + return; + } + } + + for dir in root { + let hardlink_dir = hardlink_dir.to_path_buf(); + send!(pools.get(), move || { + if let Ok(mut file) = File::open(&dir) { + if let Ok(hash) = hash::FileHash::create(&mut file) { + + let hlpath = join_path!(hardlink_dir, hash.to_string()); + + if let Ok(dst) = output_path(Path::new(&dir)) { + println!("{} -> {}", dir, hash); + + if let Some(parent) = dst.parent() { + if !parent.exists() { + if let Err(_) = fs::create_dir(parent) { + return; + } + } + } + + if !hlpath.exists() { + if let Err(_) = std::fs::copy(&dir, &hlpath) { + return; + } + } + + fs::hard_link(hlpath, dst); + } + } + } + }); + } +} diff --git a/src/pool.rs b/src/pool.rs new file mode 100644 index 0000000..7dab5c2 --- /dev/null +++ b/src/pool.rs @@ -0,0 +1,100 @@ +use std::thread; +use std::sync::{Arc, Mutex}; +use std::sync::mpsc::{channel, Sender, Receiver, SendError, RecvError}; +use std::rc::*; +use std::cell::RefCell; +use std::rc::Rc; + +#[macro_export] +macro_rules! create_pools { + ($n:expr) => { + { + let mut pools = $crate::rotating_list::RotatingList::new(); + + for _ in 0..$n { + let mut pool = $crate::pool::Pool::spawn(|x| { + funcall(x); + }); + pool.join_on_drop = true; + + pools.push(pool); + } + pools + } + } +} + +#[macro_export] +macro_rules! send { + ($i:expr, $($e:expr),+) => { + $( + $i.send($e).unwrap(); + )+ + } +} + +pub struct Pool +where T:std::marker::Send+ 'static +{ + sender: Sender>, + thread: Option>, + + pub join_on_drop: bool, +} + +impl Pool +{ + pub fn spawn(mut lam: F) -> Self { + let (tx, rx): (Sender>, Receiver>) = channel(); + + let thread = Some(thread::spawn(move || { + loop { + let value = match rx.recv() { + Ok(v) => v, + Err(_) => break, + }; + if let Some(value) = value { + lam(value); + } else { + break; + } + } + })); + + Self { + sender: tx, + thread: thread, + + join_on_drop: false, + } + } + + pub fn join(&mut self) + { + if let Some(thread) = self.thread.take() { + let _ = thread.join(); + } + } + pub fn close(&mut self) -> Result<(), SendError>> + { + self.sender.send(None) + } + + pub fn send(&mut self, value: T) -> Result<(), SendError>> + { + self.sender.send(Some(value)) + } + +} + +impl Drop for Pool +{ + fn drop(&mut self) + { + let _ = self.close(); + if self.join_on_drop { + self.join(); + } + } +} + diff --git a/src/rotating_list.rs b/src/rotating_list.rs new file mode 100644 index 0000000..6edc36c --- /dev/null +++ b/src/rotating_list.rs @@ -0,0 +1,29 @@ + + +pub struct RotatingList(usize, Vec); + +impl RotatingList +{ + pub fn new() -> Self { + Self(0, Vec::new()) + } + + pub fn push(&mut self, value: T) + { + self.1.push(value); + } + + pub fn get(&mut self) -> &mut T + { + if self.0 >= self.1.len() { + self.0 = 0; + } + self.0 += 1; + + &mut self.1[self.0-1] + } + + pub fn iter(&mut self) -> &mut Vec { + &mut self.1 + } +} diff --git a/src/traverse.rs b/src/traverse.rs new file mode 100644 index 0000000..b94858e --- /dev/null +++ b/src/traverse.rs @@ -0,0 +1,67 @@ +use std::path::Path; +use std::fs::{ReadDir, DirEntry}; +use std::vec::*; +use std::io; + +pub struct DirectoryIterator

+{ + root: P, + dirs: Vec, +} + +fn traverse

(path: P, out: &mut Vec) -> io::Result<()> +where P:AsRef +{ + for entry in std::fs::read_dir(path)? { + match entry { + Ok(entry) => out.push(entry), + Err(_) => (), + } + } + + Ok(()) +} + +impl

DirectoryIterator

+where P: AsRef +{ + pub fn begin(path: P) -> Result { + + let mut cache = Vec::new(); + + traverse(&path, &mut cache)?; + + Ok(Self{ + root: path, + dirs: cache, + }) + } + +} + +impl> Iterator for DirectoryIterator

+{ + type Item = String; + + fn next(&mut self) -> Option + { + let current = self.dirs.pop(); + + if let Some(current) = current { + let path = current.path(); + + if path.is_dir() { + let _ = traverse(&path, &mut self.dirs); + self.next() + } else { + + match path.as_path().to_str() { + Some(path) => Some(path.to_string()), + None => self.next(), + } + } + } else { + None + } + } +}