1use super::common::*;
2use super::remove_comment::remove_comment;
3use nom::bytes;
4use nom::character;
5use nom::multi;
6use nom::sequence;
7use nom::IResult;
8use std::fs;
9use std::io::Error;
10use std::io::ErrorKind;
11use std::io::Read;
12use std::path::{Path, PathBuf};
13
14pub fn read_file_with_include(path: &str) -> Result<String, Error> {
15 let path = Path::new(path);
16 if path.exists() {
17 let path = path.canonicalize().unwrap();
18 let mut dirs = Vec::<PathBuf>::new();
19 dirs.push(PathBuf::from(path.parent().unwrap()));
20 let mut ss = String::new();
21 ss += &print_work_dir_begin(&dirs);
22 ss += &read_file_with_include_core(path.as_path(), &mut dirs)?;
23 ss += &print_work_dir_end();
24 return Ok(ss);
25 } else {
26 return Err(Error::from(ErrorKind::NotFound));
27 }
28}
29
30pub fn read_file_without_include(path: &str) -> Result<String, Error> {
31 let path = Path::new(path);
32 if path.exists() {
33 return read_file_without_include_core(path);
34 } else {
35 return Err(Error::from(ErrorKind::NotFound));
36 }
37}
38
39fn read_to_string(path: &Path) -> Result<String, Error> {
40 let extent = path
41 .extension()
42 .ok_or(Error::from(ErrorKind::InvalidInput))?;
43 let extent = extent.to_string_lossy().into_owned();
44 if extent == "gz" {
45 let f = std::fs::File::open(path)?;
46 let reader = std::io::BufReader::new(f);
47 let mut reader = flate2::read::GzDecoder::new(reader);
48 let mut s = String::new();
49 reader.read_to_string(&mut s)?;
50 return Ok(s);
51 } else {
52 let s = fs::read_to_string(path)?;
53 return Ok(s);
54 }
55}
56
57fn read_file_with_include_core(path: &Path, dirs: &mut Vec<PathBuf>) -> Result<String, Error> {
58 let s = read_to_string(path)?;
59 return evaluate_include(&s, dirs);
60}
61
62pub fn read_file_without_include_core(path: &Path) -> Result<String, Error> {
63 let s = read_to_string(path)?;
64 return remove_comment_result(&s);
65}
66
67fn get_next_path(filename: &Path, dirs: &[PathBuf]) -> Option<PathBuf> {
68 for d in dirs.iter().rev() {
69 let dir = d;
70 let path = dir.join(Path::new(filename));
71 if path.exists() {
72 return Some(path);
73 }
74 }
75 return None;
76}
77
78fn print_work_dir_begin(dirs: &[PathBuf]) -> String {
79 let path = &dirs[dirs.len() - 1];
80 let path = path.as_path().to_str().unwrap();
81 return format!("WorkDirBegin \"{}\"\n", path);
82}
83
84fn print_work_dir_end() -> String {
85 return "WorkDirEnd\n".to_string();
86}
87
88fn remove_comment_result(s: &str) -> Result<String, Error> {
89 let r = remove_comment(s);
90 match r {
91 Ok((_, s)) => {
92 return Ok(s);
93 }
94 Err(e) => {
95 return Err(Error::new(ErrorKind::Other, e.to_string()));
96 }
97 }
98}
99
100fn parse_tokens(s: &str) -> Result<Vec<String>, Error> {
101 let r = nom::combinator::all_consuming(nom::multi::many0(parse_one))(&s);
102 match r {
103 Ok((_, vs)) => {
104 return Ok(vs);
105 }
106 Err(e) => {
107 return Err(Error::new(ErrorKind::Other, e.to_string()));
108 }
109 }
110}
111
112fn evaluate_include(s: &str, dirs: &mut Vec<PathBuf>) -> Result<String, Error> {
113 let s = remove_comment_result(s)?;
114 let vs = parse_tokens(&s)?;
115
116 let mut ss = String::new();
117 for s in vs {
119 if s.starts_with("Include") {
120 let vv: Vec<&str> = s.split('|').collect();
121 let filename = Path::new(vv[1]);
122 if let Some(next_path) = get_next_path(filename, dirs) {
123 dirs.push(PathBuf::from(next_path.parent().unwrap()));
124 ss += &print_work_dir_begin(dirs);
125
126 let rss = read_file_with_include_core(next_path.as_path(), dirs)?;
127 ss += &rss;
128 if vv.len() > 2 {
129 for i in 2..vv.len() {
130 ss += &format!(" {}", vv[i]);
131 }
132 ss += "\n";
133 }
134 dirs.pop();
135 ss += &print_work_dir_end();
136 } else {
137 return Err(Error::from(ErrorKind::NotFound));
138 }
139 } else {
140 ss += &s;
141 }
142 }
143 return Ok(ss);
146}
147
148fn parse_one(s: &str) -> IResult<&str, String> {
149 return nom::branch::alt((
150 parse_space1,
151 parse_string_literal,
152 parse_include,
153 parse_token,
154 parse_float,
155 parse_any,
156 ))(s);
157}
158
159fn parse_token(s: &str) -> IResult<&str, String> {
160 let (s, (a, b)) = nom::branch::permutation((
161 character::complete::alpha1,
162 bytes::complete::take_while(|c: char| c.is_alphanumeric() || c == '_'),
163 ))(s)?;
164 return Ok((s, format!("{}{}", a, b)));
165}
166
167fn parse_float(s: &str) -> IResult<&str, String> {
168 let (s, a) = nom::number::complete::recognize_float(s)?;
169 return Ok((s, a.to_string()));
170}
171
172fn parse_any(s: &str) -> IResult<&str, String> {
173 let (s, a) = character::complete::anychar(s)?;
174 return Ok((s, a.to_string()));
175}
176
177fn parse_space1(s: &str) -> IResult<&str, String> {
178 let (s, a) = character::complete::multispace1(s)?;
179 return Ok((s, a.to_string()));
180}
181
182fn parse_string_literal(s: &str) -> IResult<&str, String> {
183 let (s, a) = sequence::delimited(
184 character::complete::char('"'),
185 bytes::complete::take_until("\""),
186 character::complete::char('"'),
187 )(s)?;
188 return Ok((s, format!("{}{}{}", "\"", a, "\"")));
189}
190
191pub fn parse_params(s: &str) -> IResult<&str, String> {
192 let (s, v) = multi::separated_list0(
193 space1,
194 nom::branch::permutation((
195 sequence::terminated(string_literal, space1),
196 nom::branch::alt((parse_list, parse_listed_literal)),
197 )),
198 )(s)?;
199 let mut ss = String::new();
200 if v.len() > 0 {
201 for (key, value) in v {
202 let sv = value.join(" ");
203 ss += &format!("|\"{}\" [{}]", key, sv);
204 }
205 }
206 return Ok((s, ss));
207}
208
209fn parse_include(s: &str) -> IResult<&str, String> {
210 let (s, (op, a, params)) = nom::branch::permutation((
211 sequence::terminated(bytes::complete::tag("Include"), space1),
212 sequence::terminated(string_literal, space0),
213 parse_params,
214 ))(s)?;
215 let ss = format!("{}|{}{}", op, a, params);
216 return Ok((s, ss));
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[allow(dead_code)]
225 fn evaluate_include2(s: &str) -> IResult<&str, String> {
226 let r = nom::combinator::all_consuming(nom::multi::many0(parse_one))(s);
227 match r {
228 Ok((s, vs)) => {
229 let mut ss = String::new();
230 for rs in vs {
231 if rs.starts_with("Include") {
232 ss += &rs;
233 }
234 }
235 return Ok((s, ss));
236 }
237 Err(e) => {
238 return Err(e);
239 }
240 }
241 }
242
243 #[allow(dead_code)]
244 fn make_next_path(path: &Path, s: &str) -> Option<String> {
245 let dir = path.parent().unwrap();
246 let vv: Vec<&str> = s.split('|').collect();
247 let filename = vv[1];
248 let path2 = dir.join(Path::new(filename));
249 return Some(String::from(path2.as_path().to_str().unwrap()));
254 }
255
256 #[test]
257 fn test_001() {
258 let s = "\n
259 AttributeBegin # A\n
260 Material \"matte\" \"color Kd\" [.5 .5 .8]\n
261 Translate 0 0 -140\n
262 Shape \"trianglemesh\" \"point P\" [ -1000 -1000 0 1000 -1000 0 1000 1000 0 -1000 1000 0 ]\n
263 \"float uv\" [ 0 0 5 0 5 5 0 5 ]\n
264 \"integer indices\" [ 0 1 2 2 3 0]\n
265 Shape \"trianglemesh\" \"point P\" [ -400 -1000 -1000 -400 1000 -1000 -400 1000 1000 -400 -1000 1000 ]\n
266 \"float uv\" [ 0 0 5 0 5 5 0 5 ]\n
267 \"integer indices\" [ 0 1 2 2 3 0]\n
268 AttributeEnd\n
269 Include \"./ss.txt\"\n
270 ";
271 let (a, b) = evaluate_include2(s).unwrap();
272 assert_eq!(a, "");
273 assert_eq!(b, "Include|./ss.txt");
274 let next_ = make_next_path(Path::new("~/a.txt"), &b).unwrap();
275 assert_eq!(next_, "~/./ss.txt");
276 }
277}