turtle/interpreter/
resolver.rs1use 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}