neutralts/bif/
parse_bif_include.rs

1#![doc = include_str!("../../doc/bif-include.md")]
2
3use crate::{bif::constants::*, bif::Bif, bif::BifError, constants::*, utils::*};
4use std::collections::HashSet;
5use std::fs;
6use std::path::Path;
7
8impl<'a> Bif<'a> {
9    /*
10        {:include; file-path :}
11        {:include; {:flg; require safe noparse :} >> file-path :}
12    */
13    pub(crate) fn parse_bif_include(&mut self) -> Result<(), BifError> {
14        if self.mod_filter || self.mod_scope {
15            return Err(self.bif_error(BIF_ERROR_MODIFIER_NOT_ALLOWED));
16        }
17
18        self.extract_params_code(true);
19
20        if !self.flags.is_empty() {
21            let flags_allowed: HashSet<&str> = ["require", "safe", "noparse"].into_iter().collect();
22
23            for f in self.flags.split('|').filter(|s| !s.is_empty()) {
24                if !flags_allowed.contains(f) {
25                    return Err(self.bif_error(&format!("{} flag not allowed", f)));
26                }
27            }
28        }
29
30        self.file_path = self.code.clone();
31
32        // For security requires {:allow;
33        if self.file_path.contains(BIF_OPEN) {
34            if !self.contains_allow(&self.file_path) {
35                return Err(self.bif_error(BIF_ERROR_INSECURE_FILE_NAME));
36            }
37            self.file_path = new_child_parse!(self, &self.code, false);
38        }
39
40        if let Some(stripped) = self.file_path.strip_prefix('#') {
41            self.file_path = format!("{}{}", self.inherit.current_dir, stripped);
42        }
43
44        let path = Path::new(&self.file_path);
45        if !path.exists() {
46            if self.flags.contains("|require|") {
47                return Err(self.bif_error(BIF_ERROR_FILE_NOT_FOUND));
48            } else {
49                return Ok(());
50            }
51        }
52
53        if let Some(parent) = path.parent() {
54            self.dir = parent.display().to_string();
55        }
56
57        let canonical_path = fs::canonicalize(path)
58            .unwrap()
59            .to_string_lossy()
60            .into_owned();
61
62        if self.mod_negate && self.inherit.include_files.contains(&canonical_path) {
63            self.out = EMPTY_STRING;
64
65            return Ok(());
66        }
67
68        if self.flags.contains("|safe|") {
69            self.code = fs::read_to_string(&self.file_path).unwrap_or("".to_string());
70            self.code = escape_chars(&unescape_chars(&self.code, false), false).to_string();
71            self.code = self.code.replace(BIF_OPEN, BIF_SANITIZE_OPEN);
72            self.code = self.code.replace(BIF_CLOSE, BIF_SANITIZE_CLOSE);
73            self.out = self.code.clone();
74
75            return Ok(());
76        }
77
78        if self.flags.contains("|noparse|") {
79            self.code = fs::read_to_string(&self.file_path).unwrap_or("".to_string());
80            self.out = self.code.clone();
81
82            return Ok(());
83        }
84
85        self.inherit.include_files.push(canonical_path);
86
87        let mut file_raw = fs::read_to_string(&self.file_path).unwrap_or("".to_string());
88        if self.shared.comments.contains("remove") {
89            file_raw = remove_comments(&file_raw);
90        }
91
92        self.out = new_child_parse!(self, &file_raw, true);
93
94        Ok(())
95    }
96}
97
98#[cfg(test)]
99#[path = "parse_bif_include_tests.rs"]
100mod tests;