Skip to content

Commit 0da7439

Browse files
committed
fix(config): fixed bug where user path couldn't be parsed when using certain regex in variable
1 parent d1d4c33 commit 0da7439

5 files changed

Lines changed: 112 additions & 16 deletions

File tree

src/config/core.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,13 @@ impl TryFrom<Cli> for Config {
233233
builder = builder.add_source(config::File::from(config_file));
234234
}
235235

236-
let config = builder.build().map_err(Error::ConfigParse)?;
236+
let config = match builder.build() {
237+
Ok(c) => c,
238+
Err(e) => {
239+
error!("\n{}", NO_CONFIG_FILE_MSG);
240+
return Err(Error::ConfigParse(e));
241+
}
242+
};
237243

238244
let fixed = fix_multiple_path_subs(&config, vec!["source", "output_path"]).unwrap_or_else(|e| {
239245
match e {
@@ -243,13 +249,16 @@ impl TryFrom<Cli> for Config {
243249
vec![]
244250
});
245251

246-
let fixed_source = fixed.first().unwrap();
247-
let fixed_output_path = fixed.get(1).unwrap();
252+
let fixed_source = fixed.to_owned().first().unwrap().clone();
253+
let fixed_output_path = fixed.get(1).unwrap_or(&PathBuf::from("output.csv")).clone();
254+
255+
debug!("Fixed source: {:#?}", fixed_source);
256+
debug!("Fixed output path: {:#?}", fixed_output_path);
248257

249258
let mut config: Config = config.try_deserialize().expect("Failed to deserialize config");
250259

251-
config.source.clone_from(fixed_source);
252-
config.output_path.clone_from(fixed_output_path);
260+
config.source.clone_from(&fixed_source);
261+
config.output_path.clone_from(&fixed_output_path);
253262

254263
config = clear_placeholder_keys(config);
255264

@@ -258,18 +267,21 @@ impl TryFrom<Cli> for Config {
258267
}
259268

260269
#[allow(unused_assignments, clippy::redundant_else, clippy::manual_let_else)]
261-
fn fix_multiple_path_subs(config: &config::Config, paths: Vec<&str>) -> Result<Vec<PathBuf>> {
270+
fn fix_multiple_path_subs(config: &config::Config, keys: Vec<&str>) -> Result<Vec<PathBuf>> {
262271
let mut extracted = vec![];
263272

264273
let mut last_path: Box<&str> = Box::default();
265-
for path in paths {
274+
for path in keys {
266275
last_path = Box::new(path);
267276
debug!("Attempting to extract path: {}", path);
268277

269278
let extracted_path = extract_cached_config_value(config, path)?;
279+
debug!("Extracted path: {}", extracted_path);
270280
let fixed_path = match parse_user_variable_path(&extracted_path) {
271281
Ok(f) => f,
272282
Err(_) => {
283+
trace!("LAST PATH: {}", last_path);
284+
trace!("CURRENT PATH: {}", path);
273285
if *last_path == path {
274286
warn!("Failed to extract path: {}", path);
275287
let extension_idx = extracted_path.rfind('.');
@@ -289,6 +301,7 @@ fn fix_multiple_path_subs(config: &config::Config, paths: Vec<&str>) -> Result<V
289301
}
290302
}
291303
};
304+
debug!("Fixed path: {:#?}", fixed_path);
292305
extracted.push(fixed_path);
293306
}
294307
Ok(extracted)

src/config/file_path_finds.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,19 @@ pub static USER_PATH_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\{(.
3939
/// It's marginally faster to use an Array/slice over generic size, than allocating to the heap via `Vec::new()`;
4040
const _S: usize = 1;
4141

42-
pub fn parse_user_variable_path(path_str: &str) -> Result<PathBuf> {
43-
// If the user has not provided a user defined regex, then we can just fix the path and return it
44-
let user_defined_parts = extract_user_regex(path_str);
42+
// TODO: Test
43+
//// Substitute the `var` variable in a string with the given `val` value.
44+
////
45+
//// Variable format: `{{ var }}`
46+
// fn substitute<'a: 'b, 'b>(str: &'a str, var: &str, val: &str) -> std::borrow::Cow<'b, str> {
47+
// let format = format!(r"\{{\{{[[:space:]]*{}[[:space:]]*\}}\}}", var);
48+
// Regex::new(&format).unwrap().replace_all(str, val)
49+
// }
4550

46-
// Serpating the calls here for refactoring purposes later.
47-
// Can just call match on the extracted user_regex fnc call above to simplify if wanted.
48-
let user_defined_parts = match user_defined_parts {
51+
pub fn parse_user_variable_path(path_str: &str) -> Result<PathBuf> {
52+
let user_defined_parts = match extract_user_regex(path_str) {
4953
Some(mut parts) => {
54+
trace!("User defined parts INNER: {:?}", parts);
5055
parts.base_path = if is_relative(parts.base_path.to_str().unwrap()).is_ok() {
5156
is_relative(parts.base_path.to_str().unwrap())?
5257
} else {
@@ -57,13 +62,18 @@ pub fn parse_user_variable_path(path_str: &str) -> Result<PathBuf> {
5762
None => return is_relative(path_str),
5863
};
5964

65+
trace!("User defined parts OUTER: {:?}", user_defined_parts);
66+
6067
let base_path_parent = user_defined_parts
6168
.base_path
6269
.parent()
6370
.ok_or_else(|| Error::NoParentPath(user_defined_parts.base_path.clone()))?;
6471

72+
trace!("Base path parent: {:?}", base_path_parent);
73+
6574
let before_reg_filename =
66-
&user_defined_parts.before_regex[user_defined_parts.before_regex.rfind('\\').unwrap_or_default() + 1..];
75+
&user_defined_parts.before_regex[(user_defined_parts.before_regex.rfind('\\').unwrap_or_default() + 1)..];
76+
trace!("Before regex filename: {:?}", before_reg_filename);
6777

6878
let mut matching_files = Box::new(
6979
find_match_files_from_regex_path(base_path_parent, &user_defined_parts, before_reg_filename)
@@ -97,14 +107,20 @@ fn extract_user_regex(base_path: &str) -> Option<UserDefinedParts<'_, PathBuf>>
97107
let re = &USER_PATH_REGEX;
98108

99109
if let Some(captures) = re.captures(base_path) {
100-
let var = &captures[1];
101-
let user_defined_regex = Regex::new(var).ok()?;
110+
let inner_regx = &captures[1];
111+
let user_defined_regex = Regex::new(inner_regx).ok()?;
102112

103113
// Everything BEFORE the user defined regex (ie : before { __ } )
104114
let start = &base_path[0..captures.get(0).unwrap().start()];
105115

106116
// Everything AFTER the user defined regex (ie : after { __ } ) could be ext, or more filename
107117
let end = &base_path[captures.get(0).unwrap().end()..];
118+
let raw_ext = Some(
119+
end[end.rfind('.').unwrap_or_default()..]
120+
.split_whitespace()
121+
.next()
122+
.unwrap_or_default(),
123+
);
108124

109125
return Some(UserDefinedParts {
110126
base_path: PathBuf::from(base_path),
@@ -114,6 +130,7 @@ fn extract_user_regex(base_path: &str) -> Option<UserDefinedParts<'_, PathBuf>>
114130
_phantom: std::marker::PhantomData,
115131
},
116132
suffix_ext: Some(end),
133+
raw_ext,
117134
});
118135
}
119136

@@ -142,6 +159,7 @@ fn find_match_files_from_regex_path(
142159
let mut matches: Vec<DirEntry> = Vec::new();
143160

144161
for entry in std::fs::read_dir(base_directory).map_err(Error::Io)? {
162+
trace!("Entry: {:?}", entry);
145163
let entry = entry?;
146164
let metadata = entry.metadata()?;
147165
let filename = entry.file_name().into_string().unwrap_or_default();
@@ -208,6 +226,7 @@ mod regex_filename {
208226
_phantom: std::marker::PhantomData,
209227
},
210228
suffix_ext: Some(".csv"),
229+
raw_ext: Some(".csv"),
211230
};
212231

213232
let matches =

src/config/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use config::Value;
1010
use regex::Regex;
1111

1212
use crate::error::Error;
13+
use crate::prelude::*;
1314

1415
#[derive(Debug, Clone)]
1516
struct UserDefinedRegex<'b> {
@@ -18,6 +19,7 @@ struct UserDefinedRegex<'b> {
1819
}
1920

2021
#[derive(Debug, Clone)]
22+
#[allow(dead_code)]
2123
struct UserDefinedParts<'a, P>
2224
where
2325
P: Into<PathBuf>,
@@ -27,6 +29,7 @@ where
2729
before_regex: &'a str,
2830
user_regex: UserDefinedRegex<'a>,
2931
suffix_ext: Option<&'a str>,
32+
raw_ext: Option<&'a str>,
3033
}
3134

3235
#[allow(clippy::unnecessary_wraps)] // TODO: will need to change it over at some point
@@ -62,6 +65,7 @@ pub(crate) fn resolve_if_relative(path: &'_ Path) -> Cow<'_, Path> {
6265
}
6366

6467
pub fn extract_cached_config_value(config: &config::Config, find_key_for: &str) -> crate::prelude::Result<String> {
68+
trace!("Extracting cached config value for: {}", find_key_for);
6569
let (_key, prov_path) = config
6670
.cache
6771
.clone()

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,7 @@ pub enum Error {
122122

123123
#[error("Failed to parse path: {0}")]
124124
ParsingPath(String),
125+
126+
#[error("Failed to update the application: {0}")]
127+
SelfUpdateFailed(#[from] self_update::errors::Error),
125128
}

src/macros.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/// Allows you to pull the authors for the command from your Cargo.toml at
2+
/// compile time in the form:
3+
/// `"author1 lastname <author1@example.com>:author2 lastname <author2@example.com>"`
4+
///
5+
/// You can replace the colons with a custom separator by supplying a
6+
/// replacement string, so, for example,
7+
/// `crate_authors!(",\n")` would become
8+
/// `"author1 lastname <author1@example.com>,\nauthor2 lastname <author2@example.com>,\nauthor3 lastname <author3@example.com>"`
9+
///
10+
/// # Examples
11+
///
12+
/// ```no_run
13+
/// let m = crate_authors!();
14+
/// assert_eq!(m, "author1 lastname <author1@example.com>:author2 lastname <author2@example.com>"
15+
/// ```
16+
#[macro_export]
17+
macro_rules! crate_authors {
18+
($sep:expr) => {{
19+
static AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
20+
if AUTHORS.contains(':') {
21+
static CACHED: std::sync::OnceLock<String> = std::sync::OnceLock::new();
22+
let s = CACHED.get_or_init(|| AUTHORS.replace(':', $sep));
23+
let s: &'static str = &*s;
24+
s
25+
} else {
26+
AUTHORS
27+
}
28+
}};
29+
() => {
30+
env!("CARGO_PKG_AUTHORS")
31+
};
32+
}
33+
34+
/// Allows you to pull the name from your Cargo.toml at compile time.
35+
///
36+
/// <div class="warning">
37+
///
38+
/// **NOTE:** This macro extracts the name from an environment variable `CARGO_PKG_NAME`.
39+
/// When the crate name is set to something different from the package name,
40+
/// use environment variables `CARGO_CRATE_NAME` or `CARGO_BIN_NAME`.
41+
/// See [the Cargo Book](https://doc.rust-lang.org/cargo/reference/environment-variables.html)
42+
/// for more information.
43+
///
44+
/// </div>
45+
///
46+
/// # Examples
47+
///
48+
/// ```no_run
49+
/// let m = crate_name!();
50+
/// assert_eq!(m, "csv_parser_rs");
51+
/// ```
52+
#[macro_export]
53+
macro_rules! crate_name {
54+
() => {
55+
env!("CARGO_PKG_NAME")
56+
};
57+
}

0 commit comments

Comments
 (0)