use std::io::{self, Cursor, Read};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use structopt::StructOpt;
#[allow(missing_docs)]
#[derive(Debug, StructOpt)]
pub struct ResourceOpts {
#[structopt(
long,
number_of_values = 1,
help = "Look for code templates and other resources in DIR",
name = "DIR"
)]
resources: Vec<PathBuf>,
}
impl ResourceOpts {
pub fn handle<P: AsRef<Path>>(&self, doc_path: Option<P>) {
for rpath in &self.resources {
add_search_path(rpath);
}
if let Some(doc_path) = doc_path.as_ref() {
add_search_path(doc_path);
}
}
}
use lazy_static::lazy_static;
lazy_static! {
static ref SEARCH_PATHS: Mutex<Vec<PathBuf>> = {
let ret = Vec::new();
Mutex::new(ret)
};
}
static EMBEDDED_FILES: &[(&str, &[u8])] = include!(concat!(env!("OUT_DIR"), "/embedded_files.rs"));
pub fn embedded_files() -> &'static [(&'static str, &'static [u8])] {
EMBEDDED_FILES
}
pub fn add_search_path<P: AsRef<Path>>(path: P) {
SEARCH_PATHS
.lock()
.expect("Unable to lock SEARCH_PATHS")
.push(path.as_ref().into());
}
fn open<P: AsRef<Path>>(subpath: P, template: Option<&str>) -> io::Result<Box<dyn Read>> {
let subpath = subpath.as_ref();
let plain = match internal_open(subpath) {
Ok(r) => return Ok(r),
Err(e) => e,
};
let commonpath = Path::new("common").join(subpath);
let common = match internal_open(&commonpath) {
Ok(r) => return Ok(r),
Err(e) => e,
};
let templated = match template {
Some(templ) => {
let templpath = Path::new(templ).join(subpath);
match internal_open(&templpath) {
Ok(r) => return Ok(r),
Err(e) => Some(e),
}
}
None => None,
};
if plain.kind() != io::ErrorKind::NotFound {
return Err(plain);
}
if common.kind() != io::ErrorKind::NotFound {
return Err(common);
}
match templated {
Some(e) => Err(e),
None => Err(common),
}
}
fn internal_open(subpath: &Path) -> io::Result<Box<dyn Read>> {
let search_paths = SEARCH_PATHS.lock().expect("Unable to lock SEARCH_PATHS");
let search_paths = search_paths.iter().map(|p| p.as_path());
let search_paths = std::iter::empty().chain(search_paths);
let mut ret = Err(io::Error::new(
io::ErrorKind::NotFound,
format!("Unable to find {} in resource paths", subpath.display()),
));
for basepath in search_paths {
let full_path = basepath.join(subpath);
ret = std::fs::File::open(full_path);
if ret.is_ok() {
break;
}
}
match ret {
Ok(ret) => Ok(Box::new(ret)),
Err(e) => {
if let Some(data) = EMBEDDED_FILES.iter().find(|e| Path::new(e.0) == subpath) {
Ok(Box::new(Cursor::new(data.1)))
} else {
Err(e)
}
}
}
}
pub fn read_as_string<P: AsRef<Path>>(subpath: P, template: Option<&str>) -> io::Result<String> {
let mut f = open(subpath, template)?;
let mut ret = String::with_capacity(8192);
f.read_to_string(&mut ret)?;
Ok(ret)
}