diff options
Diffstat (limited to 'dhall_syntax/src/core/import.rs')
-rw-r--r-- | dhall_syntax/src/core/import.rs | 78 |
1 files changed, 76 insertions, 2 deletions
diff --git a/dhall_syntax/src/core/import.rs b/dhall_syntax/src/core/import.rs index d1f3fca..ef696b9 100644 --- a/dhall_syntax/src/core/import.rs +++ b/dhall_syntax/src/core/import.rs @@ -11,10 +11,15 @@ pub enum FilePrefix { Home, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct File { + pub file_path: Vec<String>, +} + /// The location of import (i.e. local vs. remote vs. environment) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ImportLocation<SubExpr> { - Local(FilePrefix, Vec<String>), + Local(FilePrefix, File), Remote(URL<SubExpr>), Env(String), Missing, @@ -24,7 +29,7 @@ pub enum ImportLocation<SubExpr> { pub struct URL<SubExpr> { pub scheme: Scheme, pub authority: String, - pub path: Vec<String>, + pub path: File, pub query: Option<String>, pub headers: Option<SubExpr>, } @@ -99,3 +104,72 @@ impl<SE> Import<SE> { }) } } + +pub trait Canonicalize { + fn canonicalize(&self) -> Self; +} + +impl Canonicalize for File { + fn canonicalize(&self) -> File { + let mut file_path = Vec::new(); + let mut file_path_components = self.file_path.clone().into_iter(); + + loop { + let component = file_path_components.next(); + match component.as_ref() { + // ─────────────────── + // canonicalize(ε) = ε + None => break, + + // canonicalize(directory₀) = directory₁ + // ─────────────────────────────────────── + // canonicalize(directory₀/.) = directory₁ + Some(c) if c == "." => continue, + + Some(c) if c == ".." => match file_path_components.next() { + // canonicalize(directory₀) = ε + // ──────────────────────────── + // canonicalize(directory₀/..) = /.. + None => file_path.push("..".to_string()), + + // canonicalize(directory₀) = directory₁/.. + // ────────────────────────────────────────────── + // canonicalize(directory₀/..) = directory₁/../.. + Some(ref c) if c == ".." => { + file_path.push("..".to_string()); + file_path.push("..".to_string()); + }, + + // canonicalize(directory₀) = directory₁/component + // ─────────────────────────────────────────────── ; If "component" is not + // canonicalize(directory₀/..) = directory₁ ; ".." + Some(_) => continue, + }, + + // canonicalize(directory₀) = directory₁ + // ───────────────────────────────────────────────────────── ; If no other + // canonicalize(directory₀/component) = directory₁/component ; rule matches + Some(c) => file_path.push(c.clone()), + } + } + + File { file_path } + } +} + +impl<SubExpr: Copy> Canonicalize for ImportLocation<SubExpr> { + fn canonicalize(&self) -> ImportLocation<SubExpr> { + match self { + ImportLocation::Local(prefix, file) => ImportLocation::Local(*prefix, file.canonicalize()), + ImportLocation::Remote(url) => ImportLocation::Remote(URL { + scheme: url.scheme, + authority: url.authority.clone(), + path: url.path.canonicalize(), + query: url.query.clone(), + headers: url.headers.clone(), + }), + ImportLocation::Env(name) => ImportLocation::Env(name.to_string()), + ImportLocation::Missing => ImportLocation::Missing, + } + } +} |