summaryrefslogtreecommitdiff
path: root/dhall
diff options
context:
space:
mode:
Diffstat (limited to 'dhall')
-rw-r--r--dhall/src/phase/binary.rs10
-rw-r--r--dhall/src/phase/resolve.rs70
2 files changed, 75 insertions, 5 deletions
diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs
index 16e7ce9..3c45e81 100644
--- a/dhall/src/phase/binary.rs
+++ b/dhall/src/phase/binary.rs
@@ -6,7 +6,7 @@ use std::vec;
use dhall_syntax::map::DupTreeMap;
use dhall_syntax::{
rc, Expr, ExprF, FilePrefix, Hash, Import, ImportLocation, ImportMode,
- Integer, InterpolatedText, Label, Natural, Scheme, URL, V, File
+ Integer, InterpolatedText, Label, Natural, Scheme, URL, V, FilePath
};
use crate::error::{DecodeError, EncodeError};
@@ -279,7 +279,7 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {
"import/remote/query".to_owned(),
))?,
};
- let file_path: Vec<_> = rest
+ let file_path = rest
.map(|s| match s.as_string() {
Some(s) => Ok(s.clone()),
None => Err(DecodeError::WrongFormatError(
@@ -287,7 +287,7 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {
)),
})
.collect::<Result<_, _>>()?;
- let path = File { file_path };
+ let path = FilePath { file_path };
ImportLocation::Remote(URL {
scheme,
authority,
@@ -306,7 +306,7 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {
"import/local/prefix".to_owned(),
))?,
};
- let file_path: Vec<_> = rest
+ let file_path = rest
.map(|s| match s.as_string() {
Some(s) => Ok(s.clone()),
None => Err(DecodeError::WrongFormatError(
@@ -314,7 +314,7 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {
)),
})
.collect::<Result<_, _>>()?;
- let path = File { file_path };
+ let path = FilePath { file_path };
ImportLocation::Local(prefix, path)
}
6 => {
diff --git a/dhall/src/phase/resolve.rs b/dhall/src/phase/resolve.rs
index 5bde68a..54a0f27 100644
--- a/dhall/src/phase/resolve.rs
+++ b/dhall/src/phase/resolve.rs
@@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
use crate::error::{Error, ImportError};
use crate::phase::{Normalized, NormalizedExpr, Parsed, Resolved};
+use dhall_syntax::{FilePath, ImportLocation, URL};
type Import = dhall_syntax::Import<NormalizedExpr>;
@@ -105,6 +106,75 @@ pub(crate) fn skip_resolve_expr(
Ok(Resolved(expr))
}
+pub trait Canonicalize {
+ fn canonicalize(&self) -> Self;
+}
+
+impl Canonicalize for FilePath {
+ fn canonicalize(&self) -> FilePath {
+ 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()),
+ }
+ }
+
+ FilePath { file_path }
+ }
+}
+
+impl<SE: Copy> Canonicalize for ImportLocation<SE> {
+ fn canonicalize(&self) -> ImportLocation<SE> {
+ 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,
+ }
+ }
+}
+
#[cfg(test)]
#[rustfmt::skip]
mod spec_tests {