diff options
Diffstat (limited to 'dhall_syntax/src')
-rw-r--r-- | dhall_syntax/src/core/import.rs | 78 | ||||
-rw-r--r-- | dhall_syntax/src/parser.rs | 22 | ||||
-rw-r--r-- | dhall_syntax/src/printer.rs | 4 |
3 files changed, 90 insertions, 14 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, + } + } +} diff --git a/dhall_syntax/src/parser.rs b/dhall_syntax/src/parser.rs index 53fd68a..b70a236 100644 --- a/dhall_syntax/src/parser.rs +++ b/dhall_syntax/src/parser.rs @@ -726,19 +726,21 @@ make_parser! { }); rule!(http_raw<URL<ParsedExpr>>; children!( - [scheme(sch), authority(auth), path(p)] => URL { + [scheme(sch), authority(auth), path(file_path)] => URL { scheme: sch, authority: auth, - path: p, + path: File { file_path }, query: None, headers: None, }, - [scheme(sch), authority(auth), path(p), query(q)] => URL { - scheme: sch, - authority: auth, - path: p, - query: Some(q), - headers: None, + [scheme(sch), authority(auth), path(file_path), query(q)] => { + URL { + scheme: sch, + authority: auth, + path: File { file_path }, + query: Some(q), + headers: None, + } }, )); @@ -791,8 +793,8 @@ make_parser! { [http(url)] => { ImportLocation::Remote(url) }, - [local((prefix, p))] => { - ImportLocation::Local(prefix, p) + [local((prefix, file_path))] => { + ImportLocation::Local(prefix, File { file_path }) }, )); diff --git a/dhall_syntax/src/printer.rs b/dhall_syntax/src/printer.rs index f3dc648..1e3b7f2 100644 --- a/dhall_syntax/src/printer.rs +++ b/dhall_syntax/src/printer.rs @@ -378,12 +378,12 @@ impl<SubExpr: Display> Display for Import<SubExpr> { }; write!(f, "{}/", prefix)?; let path: String = - path.iter().map(|c| quote_if_needed(&*c)).join("/"); + path.file_path.iter().map(|c| quote_if_needed(&*c)).join("/"); f.write_str(&path)?; } Remote(url) => { write!(f, "{}://{}/", url.scheme, url.authority,)?; - let path: String = url.path.iter().join("/"); + let path: String = url.path.file_path.iter().join("/"); f.write_str(&path)?; if let Some(q) = &url.query { write!(f, "?{}", q)? |