summaryrefslogtreecommitdiff
path: root/dhall/src/imports.rs
blob: 7810c553c3f28bb358edb6e668d1ab6d8da90b19 (plain)
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
90
91
92
93
94
95
96
97
98
99
use crate::error::Error;
use crate::expr::*;
use dhall_core::*;
use quick_error::quick_error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;

quick_error! {
    #[derive(Debug)]
    pub enum ImportError {
        Recursive(import: Import, err: Box<Error>) {}
        UnexpectedImport(import: Import) {}
    }
}

/// A root from which to resolve relative imports.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImportRoot {
    LocalDir(PathBuf),
}

fn resolve_import(
    import: &Import,
    root: &ImportRoot,
) -> Result<Resolved, ImportError> {
    use self::ImportRoot::*;
    use dhall_core::FilePrefix::*;
    use dhall_core::ImportLocation::*;
    let cwd = match root {
        LocalDir(cwd) => cwd,
    };
    match &import.location_hashed.location {
        Local(prefix, path) => {
            let path = match prefix {
                // TODO: fail gracefully
                Parent => cwd.parent().unwrap().join(path),
                Here => cwd.join(path),
                _ => unimplemented!("{:?}", import),
            };
            Ok(load_import(&path).map_err(|e| {
                ImportError::Recursive(import.clone(), Box::new(e))
            })?)
        }
        _ => unimplemented!("{:?}", import),
    }
}

fn load_import(f: &Path) -> Result<Resolved, Error> {
    Ok(Parsed::parse_file(f)?.resolve()?)
}

fn resolve_expr(
    Parsed(expr, root): Parsed,
    allow_imports: bool,
) -> Result<Resolved, ImportError> {
    let resolve = |import: &Import| -> Result<SubExpr<X, X>, ImportError> {
        if allow_imports {
            let expr = resolve_import(import, &root)?;
            Ok(expr.0)
        } else {
            Err(ImportError::UnexpectedImport(import.clone()))
        }
    };
    let expr = expr.as_ref().traverse_embed(&resolve)?;
    Ok(Resolved(expr.squash_embed()))
}

impl Parsed {
    pub fn parse_file(f: &Path) -> Result<Parsed, Error> {
        let mut buffer = String::new();
        File::open(f)?.read_to_string(&mut buffer)?;
        let expr = parse_expr(&*buffer)?;
        let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned());
        Ok(Parsed(expr, root))
    }

    pub fn parse_str(s: &str) -> Result<Parsed, Error> {
        let expr = parse_expr(s)?;
        let root = ImportRoot::LocalDir(std::env::current_dir()?);
        Ok(Parsed(expr, root))
    }

    pub fn parse_binary_file(f: &Path) -> Result<Parsed, Error> {
        let mut buffer = Vec::new();
        File::open(f)?.read_to_end(&mut buffer)?;
        let expr = crate::binary::decode(&buffer)?;
        let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned());
        Ok(Parsed(expr, root))
    }

    pub fn resolve(self) -> Result<Resolved, ImportError> {
        crate::imports::resolve_expr(self, true)
    }
    pub fn skip_resolve(self) -> Result<Resolved, ImportError> {
        crate::imports::resolve_expr(self, false)
    }
}