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 ref TEMPLATE_NAME: Mutex<Option<String>> = Mutex::new(None);
}
static EMBEDDED_FILES: &[(&str, &[u8])] = include!(concat!(env!("OUT_DIR"), "/embedded_files.inc"));
fn add_search_path<P: AsRef<Path>>(path: P) {
SEARCH_PATHS
.lock()
.expect("Unable to lock SEARCH_PATHS")
.push(path.as_ref().into());
}
pub fn set_template(template: &str) {
*TEMPLATE_NAME.lock().expect("Unable to lock TEMPLATE_NAME") = Some(template.to_string());
}
fn open<P: AsRef<Path>>(subpath: P) -> io::Result<Box<dyn Read>> {
let subpath = subpath.as_ref();
match internal_open(subpath) {
Ok(r) => Ok(r),
Err(e) => {
let template = TEMPLATE_NAME.lock().expect("Unable to lock TEMPLATE_NAME");
if let Some(templ) = template.as_deref() {
let subpath = Path::new(templ).join(subpath);
match internal_open(&subpath) {
Ok(r) => Ok(r),
Err(sub_e) => {
if sub_e.kind() != io::ErrorKind::NotFound
&& e.kind() == io::ErrorKind::NotFound
{
Err(sub_e)
} else {
Err(e)
}
}
}
} else {
Err(e)
}
}
}
}
fn internal_fallback_path() -> Option<&'static Path> {
match env!("FALLBACK_PATH") {
"" => None,
s => Some(Path::new(s)),
}
}
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)
.chain(internal_fallback_path());
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) -> io::Result<String> {
let mut f = open(subpath)?;
let mut ret = String::with_capacity(8192);
f.read_to_string(&mut ret)?;
Ok(ret)
}