1#[cfg(feature = "unstable-cli")]
9use clap::Parser;
10use std::io::{self, Cursor, Read};
11use std::path::{Path, PathBuf};
12use std::sync::Mutex;
13
14#[allow(missing_docs)]
15#[derive(Debug)]
16#[cfg_attr(feature = "unstable-cli", derive(Parser))]
17pub struct ResourceOpts {
22 #[cfg_attr(
23 feature = "unstable-cli",
24 clap(
25 long,
26 number_of_values = 1,
27 help = "Look for code templates and other resources in DIR",
28 name = "DIR"
29 )
30 )]
31 resources: Vec<PathBuf>,
32}
33
34impl ResourceOpts {
35 pub fn handle<P: AsRef<Path>>(&self, doc_path: Option<P>) {
37 for rpath in &self.resources {
38 add_search_path(rpath);
39 }
40 if let Some(doc_path) = doc_path.as_ref() {
41 add_search_path(doc_path);
42 }
43 }
44}
45
46use lazy_static::lazy_static;
47
48lazy_static! {
49 static ref SEARCH_PATHS: Mutex<Vec<PathBuf>> = {
50 let ret = Vec::new();
51 Mutex::new(ret)
52 };
53}
54
55static EMBEDDED_FILES: &[(&str, &[u8])] = include!(concat!(env!("OUT_DIR"), "/embedded_files.rs"));
56
57pub fn embedded_files() -> &'static [(&'static str, &'static [u8])] {
59 EMBEDDED_FILES
60}
61
62pub fn add_search_path<P: AsRef<Path>>(path: P) {
64 SEARCH_PATHS
65 .lock()
66 .expect("Unable to lock SEARCH_PATHS")
67 .push(path.as_ref().into());
68}
69
70fn open<P: AsRef<Path>>(subpath: P, template: Option<&str>) -> io::Result<Box<dyn Read>> {
83 let subpath = subpath.as_ref();
84 let plain = match internal_open(subpath) {
85 Ok(r) => return Ok(r),
86 Err(e) => e,
87 };
88 let commonpath = Path::new("common").join(subpath);
89 let common = match internal_open(&commonpath) {
90 Ok(r) => return Ok(r),
91 Err(e) => e,
92 };
93 let templated = match template {
94 Some(templ) => {
95 let templpath = Path::new(templ).join(subpath);
96 match internal_open(&templpath) {
97 Ok(r) => return Ok(r),
98 Err(e) => Some(e),
99 }
100 }
101 None => None,
102 };
103 if plain.kind() != io::ErrorKind::NotFound {
104 return Err(plain);
105 }
106 if common.kind() != io::ErrorKind::NotFound {
107 return Err(common);
108 }
109 match templated {
110 Some(e) => Err(e),
111 None => Err(common),
112 }
113}
114
115fn internal_open(subpath: &Path) -> io::Result<Box<dyn Read>> {
116 let search_paths = SEARCH_PATHS.lock().expect("Unable to lock SEARCH_PATHS");
117 let search_paths = search_paths.iter().map(|p| p.as_path());
118 let search_paths = std::iter::empty().chain(search_paths);
119 let mut ret = Err(io::Error::new(
120 io::ErrorKind::NotFound,
121 format!("Unable to find {} in resource paths", subpath.display()),
122 ));
123 for basepath in search_paths {
124 let full_path = basepath.join(subpath);
125 ret = std::fs::File::open(full_path);
126 if ret.is_ok() {
127 break;
128 }
129 }
130
131 match ret {
132 Ok(ret) => Ok(Box::new(ret)),
133 Err(e) => {
134 if let Some(data) = EMBEDDED_FILES.iter().find(|e| Path::new(e.0) == subpath) {
135 Ok(Box::new(Cursor::new(data.1)))
136 } else {
137 Err(e)
138 }
139 }
140 }
141}
142
143pub fn read_as_string<P: AsRef<Path>>(subpath: P, template: Option<&str>) -> io::Result<String> {
146 let mut f = open(subpath, template)?;
147 let mut ret = String::with_capacity(8192);
148 f.read_to_string(&mut ret)?;
149 Ok(ret)
150}