pbrt_r3/core/parser/
read_file.rs

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    //ss += &print_work_dir_begin(dirs);
118    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    //ss += &print_work_dir_end();
144    //print!("ss:{}", ss);
145    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    //print!("parse_include:{}", ss);
217    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        //.to_str().unwrap());
250        //let path2 = String::from(path2.to_str().unwrap());
251        //let path2 = path::absolute::(path2.);
252        //let path2 = String::from(path2.unwrap().to_str().unwrap());
253        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}