|
1 |
| -use crate::writers::file_log_writer::InfixFilter; |
2 |
| -use crate::{DeferredNow, FlexiLoggerError}; |
| 1 | +use crate::{writers::file_log_writer::InfixFilter, DeferredNow, FlexiLoggerError}; |
3 | 2 | use std::{
|
4 | 3 | ffi::{OsStr, OsString},
|
5 | 4 | ops::Add,
|
6 | 5 | path::{Path, PathBuf},
|
7 | 6 | };
|
| 7 | + |
8 | 8 | /// Builder object for specifying the name and path of the log output file.
|
9 | 9 | ///
|
10 | 10 | /// The filename is built from several partially components, using this pattern:
|
@@ -91,23 +91,56 @@ impl FileSpec {
|
91 | 91 | /// # Errors
|
92 | 92 | ///
|
93 | 93 | /// [`FlexiLoggerError::OutputBadFile`] if the given path exists and is a folder.
|
94 |
| - /// |
95 |
| - /// # Panics |
96 |
| - /// |
97 |
| - /// Panics if the basename of the given path has no filename |
| 94 | + /// [`FlexiLoggerError::BadFileSpec`] if deriving the `FileSpec` from the given path fails. |
| 95 | + #[allow(clippy::missing_panics_doc)] |
98 | 96 | pub fn try_from<P: Into<PathBuf>>(p: P) -> Result<Self, FlexiLoggerError> {
|
99 |
| - let p: PathBuf = p.into(); |
100 |
| - if p.is_dir() { |
101 |
| - Err(FlexiLoggerError::OutputBadFile) |
| 97 | + let input: PathBuf = p.into(); |
| 98 | + |
| 99 | + if input.is_dir() { |
| 100 | + Err(FlexiLoggerError::BadFileSpec("File path is a directory")) |
102 | 101 | } else {
|
103 |
| - Ok(FileSpec { |
104 |
| - directory: p.parent().unwrap(/*cannot fail*/).to_path_buf(), |
105 |
| - basename: p.file_stem().unwrap(/*ok*/).to_string_lossy().to_string(), |
106 |
| - o_discriminant: None, |
107 |
| - o_suffix: p.extension().map(|s| s.to_string_lossy().to_string()), |
108 |
| - timestamp_cfg: TimestampCfg::No, |
109 |
| - use_utc: false, |
110 |
| - }) |
| 102 | + let input_as_str = input.as_os_str().to_string_lossy(); |
| 103 | + if input_as_str.is_empty() { |
| 104 | + Err(FlexiLoggerError::BadFileSpec("File path is empty")) |
| 105 | + } else if input_as_str.ends_with('/') |
| 106 | + || input_as_str.ends_with("/.") |
| 107 | + || input_as_str.ends_with("/..") |
| 108 | + { |
| 109 | + Err(FlexiLoggerError::BadFileSpec( |
| 110 | + "Path ends with '/' or '/.' or '/..'", |
| 111 | + )) |
| 112 | + } else if input |
| 113 | + .file_name() |
| 114 | + .ok_or(FlexiLoggerError::OutputBadFile)? |
| 115 | + .to_string_lossy() |
| 116 | + .starts_with('.') |
| 117 | + && input.extension().is_none() |
| 118 | + { |
| 119 | + Err(FlexiLoggerError::BadFileSpec( |
| 120 | + "File name cannot start with '.' without an extension", |
| 121 | + )) |
| 122 | + } else { |
| 123 | + match input.parent() { |
| 124 | + None => Err(FlexiLoggerError::BadFileSpec( |
| 125 | + "File path has no parent directory", |
| 126 | + )), |
| 127 | + Some(parent) => { |
| 128 | + let filespec = FileSpec { |
| 129 | + directory: if parent.as_os_str().is_empty() { |
| 130 | + PathBuf::from(".") |
| 131 | + } else { |
| 132 | + parent.to_path_buf() |
| 133 | + }, |
| 134 | + basename: input.file_stem().unwrap(/*ok*/).to_string_lossy().to_string(), |
| 135 | + o_discriminant: None, |
| 136 | + o_suffix: input.extension().map(|s| s.to_string_lossy().to_string()), |
| 137 | + timestamp_cfg: TimestampCfg::No, |
| 138 | + use_utc: false, |
| 139 | + }; |
| 140 | + Ok(filespec) |
| 141 | + } |
| 142 | + } |
| 143 | + } |
111 | 144 | }
|
112 | 145 | }
|
113 | 146 |
|
@@ -473,6 +506,27 @@ mod test {
|
473 | 506 | assert_file_spec(&path, &PathBuf::from("."), true, "log");
|
474 | 507 | }
|
475 | 508 |
|
| 509 | + #[test] |
| 510 | + fn issue_194() { |
| 511 | + assert!(dbg!(FileSpec::try_from("")).is_err()); |
| 512 | + assert!(dbg!(FileSpec::try_from(".")).is_err()); |
| 513 | + assert!(dbg!(FileSpec::try_from("..")).is_err()); |
| 514 | + assert!(dbg!(FileSpec::try_from("/Users")).is_err()); |
| 515 | + assert!(dbg!(FileSpec::try_from("/Users/")).is_err()); |
| 516 | + assert!(dbg!(FileSpec::try_from("./f/")).is_err()); |
| 517 | + assert!(dbg!(FileSpec::try_from("./f/.")).is_err()); |
| 518 | + assert!(dbg!(FileSpec::try_from("./f/..")).is_err()); |
| 519 | + assert!(dbg!(FileSpec::try_from(".log")).is_err()); |
| 520 | + assert!(dbg!(FileSpec::try_from("./.log")).is_err()); |
| 521 | + assert!(dbg!(FileSpec::try_from("./f/.log")).is_err()); |
| 522 | + |
| 523 | + let filespec = FileSpec::try_from("test.log").unwrap(); |
| 524 | + std::fs::create_dir_all(filespec.get_directory()).unwrap(); |
| 525 | + assert!(std::fs::metadata(filespec.get_directory()) |
| 526 | + .unwrap() |
| 527 | + .is_dir()); |
| 528 | + } |
| 529 | + |
476 | 530 | // todo: does not support suppress_timestamp & suppress_basename & use discriminant
|
477 | 531 | fn assert_file_spec(path: &Path, folder: &Path, with_timestamp: bool, suffix: &str) {
|
478 | 532 | // check folder
|
|
0 commit comments