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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use clap::Parser;
use std::io::{self, Cursor, Read};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
#[allow(missing_docs)]
#[derive(Debug, Parser)]
pub struct ResourceOpts {
#[clap(
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)
}