turtle/interpreter/
resolver.rs

1use crate::{
2    exp, parse, stdlib, CallSnapshot, Environment, Exception, ExceptionValue as EV, Expression,
3};
4use relative_path::RelativePath;
5use std::env;
6use std::fs;
7use std::path::PathBuf;
8
9use crate::Locker;
10
11pub fn resolve_resource(
12    path: &str,
13    snapshot: Locker<CallSnapshot>,
14    via: &Expression,
15    env: Locker<Environment>,
16) -> Result<Expression, Exception> {
17    let content = match path.starts_with('@') {
18        true => match stdlib::get_std_resource(path) {
19            Some(val) => val,
20            None => exp!(
21                EV::InvalidIncludePath(String::from(path)),
22                snapshot,
23                format!("`{}` is not in the standard library", path)
24            ),
25        },
26        false => {
27            let source_path_opt = match via.source() {
28                Some(source) => match source.location() {
29                    Some(location) => Some(location),
30                    None => None,
31                },
32                None => None,
33            };
34
35            let working_dir = match env::current_dir() {
36                Ok(dir) => dir,
37                Err(_) => exp!(
38                    EV::InvalidIncludePath(String::from(path)),
39                    snapshot,
40                    "could not establish working directory (the environment is unknown)"
41                        .to_string()
42                ),
43            };
44
45            let relative_dir = match source_path_opt {
46                Some(source_path) => match fs::metadata(&source_path) {
47                    Ok(metadata) => match metadata.is_dir() {
48                        true => PathBuf::from(source_path),
49                        false => match PathBuf::from(source_path).parent() {
50                            Some(parent) => PathBuf::from(parent),
51                            None => working_dir,
52                        },
53                    },
54                    Err(_) => working_dir,
55                },
56                None => working_dir,
57            };
58
59            let relative_dir_composed = match RelativePath::from_path(&path) {
60                Ok(relative) => relative,
61                Err(err) => exp!(
62                    EV::InvalidIncludePath(String::from(path)),
63                    snapshot,
64                    format!(
65                        "could not understand include path ({}; all includes must be relative)",
66                        err
67                    )
68                ),
69            };
70
71            match fs::read_to_string(&relative_dir_composed.to_path(relative_dir)) {
72                Ok(value) => value,
73                Err(val) => exp!(
74                    EV::InvalidIncludePath(path.to_string()),
75                    snapshot,
76                    format!("unable to read file ({})", val)
77                ),
78            }
79        }
80    };
81
82    let parsed = parse(&content, &path.to_string())?;
83
84    let mut return_val = Expression::nil();
85    for exp in parsed {
86        return_val = exp.eval(CallSnapshot::new(&exp, &snapshot)?, env.clone())?;
87    }
88    Ok(return_val)
89}