History

feat!: allow to ignore IO errors when reading configuration files. (#790)

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).

Sebastian Thiel committed 3 years ago

Commit e55f4ee1 parent 8f2accd6 files · +53 −9Browse files

Unified · Split

6 files
  1. Cargo.lock +1 −0
  2. gix-config/Cargo.toml +1 −0
  3. gix-config/src/file/init/comfort.rs +1 −1
  4. gix-config/src/file/init/from_paths.rs +45 −7
  5. gix-config/src/file/init/types.rs +4 −0
  6. gix-config/tests/file/init/from_paths/mod.rs +1 −1
Cargo.lock+1−0
@@ -1447,6 +1447,7 @@ dependencies = [ · 1446 unmodified lines
1447 "gix-path 0.7.2",1447 "gix-path 0.7.2",
1448 "gix-ref 0.27.1",1448 "gix-ref 0.27.1",
1449 "gix-sec 0.6.2",1449 "gix-sec 0.6.2",
1450 "log",
1450 "memchr",1451 "memchr",
1451 "nom",1452 "nom",
1452 "once_cell",1453 "once_cell",
gix-config/Cargo.toml+1−0
@@ -24,6 +24,7 @@ gix-sec = { version = "^0.6.2", path = "../gix-sec" } · 23 unmodified lines
24gix-ref = { version = "^0.27.0", path = "../gix-ref" }24gix-ref = { version = "^0.27.0", path = "../gix-ref" }
25gix-glob = { version = "^0.5.5", path = "../gix-glob" }25gix-glob = { version = "^0.5.5", path = "../gix-glob" }
26 26
27log = "0.4.17"
27nom = { version = "7", default_features = false, features = [ "std" ] }28nom = { version = "7", default_features = false, features = [ "std" ] }
28memchr = "2"29memchr = "2"
29thiserror = "1.0.26"30thiserror = "1.0.26"
gix-config/src/file/init/comfort.rs+1−1
@@ -122,7 +122,7 @@ impl File<'static> { · 121 unmodified lines
122 branch_name: None,122 branch_name: None,
123 },123 },
124 ),124 ),
125 lossy: false,125 ..Default::default()
126 };126 };
127 127
128 let mut globals = Self::from_globals()?;128 let mut globals = Self::from_globals()?;
gix-config/src/file/init/from_paths.rs+45−7
@@ -9,8 +9,11 @@ use crate::{ · 8 unmodified lines
9#[derive(Debug, thiserror::Error)]9#[derive(Debug, thiserror::Error)]
10#[allow(missing_docs)]10#[allow(missing_docs)]
11pub enum Error {11pub enum Error {
12 #[error(transparent)]12 #[error("The configuration file at \"{}\" could not be read", path.display())]
13 Io(#[from] std::io::Error),13 Io {
14 source: std::io::Error,
15 path: std::path::PathBuf,
16 },
14 #[error(transparent)]17 #[error(transparent)]
15 Init(#[from] init::Error),18 Init(#[from] init::Error),
16}19}
@@ -22,10 +25,22 @@ impl File<'static> { · 5 unmodified lines
22 /// Note that the path will be checked for ownership to derive trust.25 /// Note that the path will be checked for ownership to derive trust.
23 pub fn from_path_no_includes(path: impl Into<std::path::PathBuf>, source: crate::Source) -> Result<Self, Error> {26 pub fn from_path_no_includes(path: impl Into<std::path::PathBuf>, source: crate::Source) -> Result<Self, Error> {
24 let path = path.into();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 let mut buf = Vec::new();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 Ok(File::from_bytes_owned(45 Ok(File::from_bytes_owned(
31 &mut buf,46 &mut buf,
@@ -69,14 +84,37 @@ impl File<'static> { · 37 unmodified lines
69 }84 }
70 85
71 buf.clear();86 buf.clear();
72 std::io::copy(87 match std::io::copy(
73 &mut match std::fs::File::open(&path) {88 &mut match std::fs::File::open(&path) {
74 Ok(f) => f,89 Ok(f) => f,
75 Err(err) if !err_on_non_existing_paths && err.kind() == std::io::ErrorKind::NotFound => continue,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 buf,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 meta.path = Some(path);118 meta.path = Some(path);
81 119
82 let config = Self::from_bytes_owned(buf, meta, options)?;120 let config = Self::from_bytes_owned(buf, meta, options)?;
gix-config/src/file/init/types.rs+4−0
@@ -22,6 +22,10 @@ pub struct Options<'a> { · 21 unmodified lines
22 /// Note that doing so will degenerate [`write_to()`][crate::File::write_to()] and strip it off its comments22 /// Note that doing so will degenerate [`write_to()`][crate::File::write_to()] and strip it off its comments
23 /// and additional whitespace entirely, but will otherwise be a valid configuration file.23 /// and additional whitespace entirely, but will otherwise be a valid configuration file.
24 pub lossy: bool,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
27impl Options<'_> {31impl Options<'_> {
gix-config/tests/file/init/from_paths/mod.rs+1−1
@@ -18,7 +18,7 @@ mod from_path_no_includes { · 17 unmodified lines
18 18
19 let err = gix_config::File::from_path_no_includes(config_path, gix_config::Source::Local).unwrap_err();19 let err = gix_config::File::from_path_no_includes(config_path, gix_config::Source::Local).unwrap_err();
20 assert!(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