Resolve `{file:path}` patterns by reading file contents. Skips patterns on commented lines. Resolves relative paths from `base_dir`.
(text: &str, base_dir: &Path)
| 406 | /// Resolve `{file:path}` patterns by reading file contents. |
| 407 | /// Skips patterns on commented lines. Resolves relative paths from `base_dir`. |
| 408 | fn resolve_file_references(text: &str, base_dir: &Path) -> Result<String> { |
| 409 | let re = regex::Regex::new(r"\{file:([^}]+)\}").unwrap(); |
| 410 | |
| 411 | let mut result = text.to_string(); |
| 412 | |
| 413 | // Collect all matches first to avoid borrow issues |
| 414 | let matches: Vec<(String, String)> = re |
| 415 | .captures_iter(text) |
| 416 | .map(|caps| { |
| 417 | let full_match = caps.get(0).unwrap().as_str().to_string(); |
| 418 | let file_path_str = caps[1].to_string(); |
| 419 | (full_match, file_path_str) |
| 420 | }) |
| 421 | .collect(); |
| 422 | |
| 423 | for (full_match, file_path_str) in matches { |
| 424 | // Check if the match is on a commented line |
| 425 | let match_start = match text.find(&full_match) { |
| 426 | Some(pos) => pos, |
| 427 | None => continue, |
| 428 | }; |
| 429 | let line_start = text[..match_start].rfind('\n').map(|p| p + 1).unwrap_or(0); |
| 430 | let line_end = text[line_start..] |
| 431 | .find('\n') |
| 432 | .map(|p| line_start + p) |
| 433 | .unwrap_or(text.len()); |
| 434 | let line = &text[line_start..line_end]; |
| 435 | if line.trim().starts_with("//") { |
| 436 | continue; |
| 437 | } |
| 438 | |
| 439 | // Resolve the file path |
| 440 | let resolved = if file_path_str.starts_with("~/") { |
| 441 | dirs::home_dir() |
| 442 | .unwrap_or_else(|| PathBuf::from("~")) |
| 443 | .join(&file_path_str[2..]) |
| 444 | } else if Path::new(&file_path_str).is_absolute() { |
| 445 | PathBuf::from(&file_path_str) |
| 446 | } else { |
| 447 | base_dir.join(&file_path_str) |
| 448 | }; |
| 449 | |
| 450 | // Read the file |
| 451 | let content = fs::read_to_string(&resolved).with_context(|| { |
| 452 | format!( |
| 453 | "bad file reference: \"{}\" - {} does not exist", |
| 454 | full_match, |
| 455 | resolved.display() |
| 456 | ) |
| 457 | })?; |
| 458 | let content = content.trim(); |
| 459 | |
| 460 | // Escape for JSON string context (newlines, quotes) |
| 461 | let escaped = content |
| 462 | .replace('\\', "\\\\") |
| 463 | .replace('"', "\\\"") |
| 464 | .replace('\n', "\\n") |
| 465 | .replace('\r', "\\r") |