Unified · Split
| @@ -1,6 +1,20 @@ | |||
| 1 | #![allow(missing_docs)] | 1 | #![allow(missing_docs)] |
| 2 | use std::path::Path; | 2 | use std::path::Path; |
| 3 | 3 | ||
| 4 | #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)] | ||
| 5 | pub struct Options { | ||
| 6 | /// How often to retry if an interrupt happens. | ||
| 7 | retries_on_interrupt: usize, | ||
| 8 | } | ||
| 9 | |||
| 10 | impl Default for Options { | ||
| 11 | fn default() -> Self { | ||
| 12 | Options { | ||
| 13 | retries_on_interrupt: 10, | ||
| 14 | } | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 4 | mod error { | 18 | mod error { |
| 5 | use quick_error::quick_error; | 19 | use quick_error::quick_error; |
| 6 | use std::path::PathBuf; | 20 | use std::path::PathBuf; |
| @@ -37,20 +51,28 @@ pub use error::Error; · 30 unmodified lines | |||
| 37 | /// * `Some(Err(Error::Permanent))` is yielded exactly once on failure. | 51 | /// * `Some(Err(Error::Permanent))` is yielded exactly once on failure. |
| 38 | pub struct Iter<'a> { | 52 | pub struct Iter<'a> { |
| 39 | cursors: Vec<&'a Path>, | 53 | cursors: Vec<&'a Path>, |
| 54 | options: Options, | ||
| 40 | } | 55 | } |
| 41 | 56 | ||
| 42 | impl<'a> Iter<'a> { | 57 | impl<'a> Iter<'a> { |
| 43 | pub fn new(target: &'a Path) -> Self { | 58 | pub fn new(target: &'a Path) -> Self { |
| 44 | Iter { cursors: vec![target] } | 59 | Iter { |
| 60 | cursors: vec![target], | ||
| 61 | options: Default::default(), | ||
| 62 | } | ||
| 45 | } | 63 | } |
| 46 | 64 | ||
| 47 | fn fail_permanently(&mut self, dir: &Path, err: impl Into<std::io::Error>) -> Option<Result<&'a Path, Error>> { | 65 | fn pernanent_failure(&mut self, dir: &Path, err: impl Into<std::io::Error>) -> Option<Result<&'a Path, Error>> { |
| 48 | self.cursors.clear(); | 66 | self.cursors.clear(); |
| 49 | Some(Err(Error::Permanent { | 67 | Some(Err(Error::Permanent { |
| 50 | err: err.into(), | 68 | err: err.into(), |
| 51 | dir: dir.to_owned(), | 69 | dir: dir.to_owned(), |
| 52 | })) | 70 | })) |
| 53 | } | 71 | } |
| 72 | |||
| 73 | fn intermediate_failure(&self, err: std::io::Error) -> Option<Result<&'a Path, Error>> { | ||
| 74 | Some(Err(Error::Intermediate(err.kind()))) | ||
| 75 | } | ||
| 54 | } | 76 | } |
| 55 | 77 | ||
| 56 | impl<'a> Iterator for Iter<'a> { | 78 | impl<'a> Iterator for Iter<'a> { |
| @@ -59,17 +81,23 @@ impl<'a> Iterator for Iter<'a> { · 2 unmodified lines | |||
| 59 | fn next(&mut self) -> Option<Self::Item> { | 81 | fn next(&mut self) -> Option<Self::Item> { |
| 60 | use std::io::ErrorKind::*; | 82 | use std::io::ErrorKind::*; |
| 61 | match self.cursors.pop() { | 83 | match self.cursors.pop() { |
| 62 | Some(cursor) => match std::fs::create_dir(cursor) { | 84 | Some(dir) => match std::fs::create_dir(dir) { |
| 63 | Ok(()) => Some(Ok(cursor)), | 85 | Ok(()) => Some(Ok(dir)), |
| 64 | Err(err) => match err.kind() { | 86 | Err(err) => match err.kind() { |
| 65 | AlreadyExists => Some(Ok(cursor)), | 87 | AlreadyExists => Some(Ok(dir)), |
| 66 | NotFound => { | 88 | NotFound => { |
| 67 | self.cursors.push(cursor); | 89 | self.cursors.push(dir); |
| 68 | self.cursors.push(match cursor.parent() { | 90 | self.cursors.push(match dir.parent() { |
| 69 | None => return self.fail_permanently(cursor, InvalidInput), | 91 | None => return self.pernanent_failure(dir, InvalidInput), |
| 70 | Some(parent) => parent, | 92 | Some(parent) => parent, |
| 71 | }); | 93 | }); |
| 72 | Some(Err(Error::Intermediate(err.kind()))) | 94 | self.intermediate_failure(err) |
| 95 | } | ||
| 96 | Interrupted if self.options.retries_on_interrupt == 0 => self.pernanent_failure(dir, Interrupted), | ||
| 97 | Interrupted => { | ||
| 98 | self.options.retries_on_interrupt -= 1; | ||
| 99 | self.cursors.push(dir); | ||
| 100 | self.intermediate_failure(err) | ||
| 73 | } | 101 | } |
| 74 | kind => todo!("{:?}", kind), | 102 | kind => todo!("{:?}", kind), |
| 75 | }, | 103 | }, |