1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use crate::{
    exp, parse, stdlib, CallSnapshot, Environment, Exception, ExceptionValue as EV, Expression,
};
use relative_path::RelativePath;
use std::env;
use std::fs;
use std::path::PathBuf;

use crate::Locker;

pub fn resolve_resource(
    path: &str,
    snapshot: Locker<CallSnapshot>,
    via: &Expression,
    env: Locker<Environment>,
) -> Result<Expression, Exception> {
    let content = match path.starts_with('@') {
        true => match stdlib::get_std_resource(path) {
            Some(val) => val,
            None => exp!(
                EV::InvalidIncludePath(String::from(path)),
                snapshot,
                format!("`{}` is not in the standard library", path)
            ),
        },
        false => {
            let source_path_opt = match via.source() {
                Some(source) => match source.location() {
                    Some(location) => Some(location),
                    None => None,
                },
                None => None,
            };

            let working_dir = match env::current_dir() {
                Ok(dir) => dir,
                Err(_) => exp!(
                    EV::InvalidIncludePath(String::from(path)),
                    snapshot,
                    "could not establish working directory (the environment is unknown)"
                        .to_string()
                ),
            };

            let relative_dir = match source_path_opt {
                Some(source_path) => match fs::metadata(&source_path) {
                    Ok(metadata) => match metadata.is_dir() {
                        true => PathBuf::from(source_path),
                        false => match PathBuf::from(source_path).parent() {
                            Some(parent) => PathBuf::from(parent),
                            None => working_dir,
                        },
                    },
                    Err(_) => working_dir,
                },
                None => working_dir,
            };

            let relative_dir_composed = match RelativePath::from_path(&path) {
                Ok(relative) => relative,
                Err(err) => exp!(
                    EV::InvalidIncludePath(String::from(path)),
                    snapshot,
                    format!(
                        "could not understand include path ({}; all includes must be relative)",
                        err
                    )
                ),
            };

            match fs::read_to_string(&relative_dir_composed.to_path(relative_dir)) {
                Ok(value) => value,
                Err(val) => exp!(
                    EV::InvalidIncludePath(path.to_string()),
                    snapshot,
                    format!("unable to read file ({})", val)
                ),
            }
        }
    };

    let parsed = parse(&content, &path.to_string())?;

    let mut return_val = Expression::nil();
    for exp in parsed {
        return_val = exp.eval(CallSnapshot::new(&exp, &snapshot)?, env.clone())?;
    }
    Ok(return_val)
}