1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
#![doc = include_str!("../README.md")]
#[cfg(test)]
extern crate indoc;
#[cfg(test)]
extern crate rstest;
#[cfg(test)]
extern crate temp_dir;
extern crate thiserror;
mod canonical_path;
mod dependency_path;
mod includes;
mod loader;
use loader::Loader;
use std::path::{Path, PathBuf};
/// Load the given file path and recursively follow references to other files
/// inside it, inserting the text from references.
///
/// References are either `${include("<path>")}` or `${include_indent("<path>")}`,
/// with the latter preserving local indentation for each new line in the referenced
/// file. Paths can be relative or absolute.
///
/// The function will check references for cyclic dependencies and will return a [Error::CyclicDependency] should it detect one.
///
/// # Example
///
/// Given the following files...
///
/// start.txt:
/// ```text
/// START
/// ${include_indent("mid.txt")}
/// ```
///
/// mid.txt:
/// ```text
/// MIDDLE 1
/// MIDDLE 2
/// ${include("end.txt")}
/// ```
///
/// end.txt:
/// ```text
/// END
/// ```
///
/// Then after loading the `start.txt` file, you'll get all files combined.
///
/// ```
/// use recursive_file_loader::load_file_recursively;
/// use indoc::indoc;
/// # use temp_dir::TempDir;
/// # let dir = TempDir::new().unwrap();
/// # let start = dir.child("start.txt");
/// # let mid = dir.child("mid.txt");
/// # let end = dir.child("end.txt");
/// # std::fs::write(
/// # &start,
/// # "START\n ${include_indent(\"mid.txt\")}".as_bytes(),
/// # ).unwrap();
/// # std::fs::write(
/// # &mid,
/// # "MIDDLE 1\nMIDDLE 2\n${include(\"end.txt\")}".as_bytes(),
/// # ).unwrap();
/// # std::fs::write(
/// # &end,
/// # "END".as_bytes(),
/// # ).unwrap();
///
/// let path = "start.txt";
/// # let path = &start;
///
/// let result = load_file_recursively(&path).unwrap();
///
/// assert_eq!(&result, indoc!("
/// START
/// MIDDLE 1
/// MIDDLE 2
/// END")
/// );
/// ```
///
/// Note that the indentation in `start.txt` has been applied to everything `start.txt` included.
pub fn load_file_recursively<P: AsRef<Path>>(origin: P) -> Result<String, Error> {
Loader::new().load_file_recursively(origin)
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("file not found: '{0}'")]
FileNotFound(PathBuf),
#[error("cyclic dependency detected between '{0}' and '{1}'")]
CyclicDependency(PathBuf, PathBuf),
#[error("IO Error")]
IOError(#[from] std::io::Error),
}