Sometimes, IO errors can occour consistently when reading from certain paths (on Windows, really), and it should be possible to not only see which path that is but also to ignore them entirely. Now IO errors contain the path of the failed configuration file. There is also a new option flag to ignore errors (off by default).
Unified · Split
| @@ -1447,6 +1447,7 @@ dependencies = [ · 1446 unmodified lines | ||
| 1447 | 1447 | "gix-path 0.7.2", |
| 1448 | 1448 | "gix-ref 0.27.1", |
| 1449 | 1449 | "gix-sec 0.6.2", |
| 1450 | + "log", | |
| 1450 | 1451 | "memchr", |
| 1451 | 1452 | "nom", |
| 1452 | 1453 | "once_cell", |
| @@ -24,6 +24,7 @@ gix-sec = { version = "^0.6.2", path = "../gix-sec" } · 23 unmodified lines | ||
| 24 | 24 | gix-ref = { version = "^0.27.0", path = "../gix-ref" } |
| 25 | 25 | gix-glob = { version = "^0.5.5", path = "../gix-glob" } |
| 26 | 26 | |
| 27 | +log = "0.4.17" | |
| 27 | 28 | nom = { version = "7", default_features = false, features = [ "std" ] } |
| 28 | 29 | memchr = "2" |
| 29 | 30 | thiserror = "1.0.26" |
| @@ -122,7 +122,7 @@ impl File<'static> { · 121 unmodified lines | ||
| 122 | 122 | branch_name: None, |
| 123 | 123 | }, |
| 124 | 124 | ), |
| 125 | − lossy: false, | |
| 125 | + ..Default::default() | |
| 126 | 126 | }; |
| 127 | 127 | |
| 128 | 128 | let mut globals = Self::from_globals()?; |
| @@ -9,8 +9,11 @@ use crate::{ · 8 unmodified lines | ||
| 9 | 9 | #[derive(Debug, thiserror::Error)] |
| 10 | 10 | #[allow(missing_docs)] |
| 11 | 11 | pub enum Error { |
| 12 | − #[error(transparent)] | |
| 13 | − Io(#[from] std::io::Error), | |
| 12 | + #[error("The configuration file at \"{}\" could not be read", path.display())] | |
| 13 | + Io { | |
| 14 | + source: std::io::Error, | |
| 15 | + path: std::path::PathBuf, | |
| 16 | + }, | |
| 14 | 17 | #[error(transparent)] |
| 15 | 18 | Init(#[from] init::Error), |
| 16 | 19 | } |
| @@ -22,10 +25,22 @@ impl File<'static> { · 5 unmodified lines | ||
| 22 | 25 | /// Note that the path will be checked for ownership to derive trust. |
| 23 | 26 | pub fn from_path_no_includes(path: impl Into<std::path::PathBuf>, source: crate::Source) -> Result<Self, Error> { |
| 24 | 27 | let path = path.into(); |
| 25 | − let trust = gix_sec::Trust::from_path_ownership(&path)?; | |
| 28 | + let trust = match gix_sec::Trust::from_path_ownership(&path) { | |
| 29 | + Ok(t) => t, | |
| 30 | + Err(err) => return Err(Error::Io { source: err, path }), | |
| 31 | + }; | |
| 26 | 32 | |
| 27 | 33 | let mut buf = Vec::new(); |
| 28 | − std::io::copy(&mut std::fs::File::open(&path)?, &mut buf)?; | |
| 34 | + match std::io::copy( | |
| 35 | + &mut match std::fs::File::open(&path) { | |
| 36 | + Ok(f) => f, | |
| 37 | + Err(err) => return Err(Error::Io { source: err, path }), | |
| 38 | + }, | |
| 39 | + &mut buf, | |
| 40 | + ) { | |
| 41 | + Ok(_) => {} | |
| 42 | + Err(err) => return Err(Error::Io { source: err, path }), | |
| 43 | + } | |
| 29 | 44 | |
| 30 | 45 | Ok(File::from_bytes_owned( |
| 31 | 46 | &mut buf, |
| @@ -69,14 +84,37 @@ impl File<'static> { · 37 unmodified lines | ||
| 69 | 84 | } |
| 70 | 85 | |
| 71 | 86 | buf.clear(); |
| 72 | − std::io::copy( | |
| 87 | + match std::io::copy( | |
| 73 | 88 | &mut match std::fs::File::open(&path) { |
| 74 | 89 | Ok(f) => f, |
| 75 | 90 | Err(err) if !err_on_non_existing_paths && err.kind() == std::io::ErrorKind::NotFound => continue, |
| 76 | − Err(err) => return Err(err.into()), | |
| 91 | + Err(err) => { | |
| 92 | + let err = Error::Io { source: err, path }; | |
| 93 | + if options.ignore_io_errors { | |
| 94 | + log::warn!("ignoring: {err:#?}"); | |
| 95 | + continue; | |
| 96 | + } else { | |
| 97 | + return Err(err); | |
| 98 | + } | |
| 99 | + } | |
| 77 | 100 | }, |
| 78 | 101 | buf, |
| 79 | − )?; | |
| 102 | + ) { | |
| 103 | + Ok(_) => {} | |
| 104 | + Err(err) => { | |
| 105 | + if options.ignore_io_errors { | |
| 106 | + log::warn!( | |
| 107 | + "ignoring: {:#?}", | |
| 108 | + Error::Io { | |
| 109 | + source: err, | |
| 110 | + path: path.clone() | |
| 111 | + } | |
| 112 | + ); | |
| 113 | + } else { | |
| 114 | + return Err(Error::Io { source: err, path }); | |
| 115 | + } | |
| 116 | + } | |
| 117 | + }; | |
| 80 | 118 | meta.path = Some(path); |
| 81 | 119 | |
| 82 | 120 | let config = Self::from_bytes_owned(buf, meta, options)?; |
| @@ -22,6 +22,10 @@ pub struct Options<'a> { · 21 unmodified lines | ||
| 22 | 22 | /// Note that doing so will degenerate [`write_to()`][crate::File::write_to()] and strip it off its comments |
| 23 | 23 | /// and additional whitespace entirely, but will otherwise be a valid configuration file. |
| 24 | 24 | pub lossy: bool, |
| 25 | + /// If true, any IO error happening when reading a configuration file will be ignored. | |
| 26 | + /// | |
| 27 | + /// That way it's possible to pass multiple files and read as many as possible, to have 'something' instead of nothing. | |
| 28 | + pub ignore_io_errors: bool, | |
| 25 | 29 | } |
| 26 | 30 | |
| 27 | 31 | impl Options<'_> { |
| @@ -18,7 +18,7 @@ mod from_path_no_includes { · 17 unmodified lines | ||
| 18 | 18 | |
| 19 | 19 | let err = gix_config::File::from_path_no_includes(config_path, gix_config::Source::Local).unwrap_err(); |
| 20 | 20 | assert!( |
| 21 | − matches!(err, gix_config::file::init::from_paths::Error::Io(io_error) if io_error.kind() == std::io::ErrorKind::NotFound) | |
| 21 | + matches!(err, gix_config::file::init::from_paths::Error::Io{source: io_error, ..} if io_error.kind() == std::io::ErrorKind::NotFound) | |
| 22 | 22 | ); |
| 23 | 23 | } |
| 24 | 24 | |