summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dhall/src/phase/binary.rs29
-rw-r--r--dhall/src/phase/resolve.rs11
-rw-r--r--dhall_syntax/src/core/import.rs122
-rw-r--r--dhall_syntax/src/parser.rs41
-rw-r--r--dhall_syntax/src/printer.rs10
5 files changed, 180 insertions, 33 deletions
diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs
index f4a9cc1..6bdc3f9 100644
--- a/dhall/src/phase/binary.rs
+++ b/dhall/src/phase/binary.rs
@@ -1,12 +1,13 @@
use itertools::Itertools;
use serde_cbor::value::value as cbor;
use std::iter::FromIterator;
+use std::vec;
use dhall_syntax::map::DupTreeMap;
use dhall_syntax::{
rc, ExprF, FilePrefix, Hash, Import, ImportHashed, ImportLocation,
ImportMode, Integer, InterpolatedText, Label, Natural, Scheme, SubExpr,
- URL, V,
+ URL, V, Directory, File,
};
use crate::error::{DecodeError, EncodeError};
@@ -290,7 +291,7 @@ fn cbor_value_to_dhall(
"import/remote/query".to_owned(),
))?,
};
- let path = rest
+ let mut components: Vec<_> = rest
.map(|s| match s.as_string() {
Some(s) => Ok(s.clone()),
None => Err(DecodeError::WrongFormatError(
@@ -298,6 +299,11 @@ fn cbor_value_to_dhall(
)),
})
.collect::<Result<_, _>>()?;
+ let file = components.pop().ok_or(
+ DecodeError::WrongFormatError(
+ "import/remote/path".to_owned(),
+ ))?;
+ let path = File { directory: Directory { components: components }, file: file };
ImportLocation::Remote(URL {
scheme,
authority,
@@ -316,7 +322,7 @@ fn cbor_value_to_dhall(
"import/local/prefix".to_owned(),
))?,
};
- let path = rest
+ let mut components: Vec<_> = rest
.map(|s| match s.as_string() {
Some(s) => Ok(s.clone()),
None => Err(DecodeError::WrongFormatError(
@@ -324,6 +330,11 @@ fn cbor_value_to_dhall(
)),
})
.collect::<Result<_, _>>()?;
+ let file = components.pop().ok_or(
+ DecodeError::WrongFormatError(
+ "import/remote/path".to_owned(),
+ ))?;
+ let path = File { directory: Directory { components: components }, file: file };
ImportLocation::Local(prefix, path)
}
6 => {
@@ -585,8 +596,8 @@ where
use serde::ser::SerializeSeq;
let count = 4 + match &import.location_hashed.location {
- ImportLocation::Remote(url) => 3 + url.path.len(),
- ImportLocation::Local(_, path) => path.len(),
+ ImportLocation::Remote(url) => 3 + url.path.clone().into_iter().len(),
+ ImportLocation::Local(_, path) => path.clone().into_iter().len(),
ImportLocation::Env(_) => 1,
ImportLocation::Missing => 0,
};
@@ -641,8 +652,8 @@ where
}
};
ser_seq.serialize_element(&url.authority)?;
- for p in &url.path {
- ser_seq.serialize_element(p)?;
+ for p in url.path.clone().into_iter() {
+ ser_seq.serialize_element(&p)?;
}
match &url.query {
None => ser_seq.serialize_element(&Null)?,
@@ -650,8 +661,8 @@ where
};
}
ImportLocation::Local(_, path) => {
- for p in path {
- ser_seq.serialize_element(p)?;
+ for p in path.clone().into_iter() {
+ ser_seq.serialize_element(&p)?;
}
}
ImportLocation::Env(env) => {
diff --git a/dhall/src/phase/resolve.rs b/dhall/src/phase/resolve.rs
index abcee7e..ac264e6 100644
--- a/dhall/src/phase/resolve.rs
+++ b/dhall/src/phase/resolve.rs
@@ -16,6 +16,7 @@ type ImportCache = HashMap<Import, Normalized>;
pub type ImportStack = Vec<Import>;
+
fn resolve_import(
import: &Import,
root: &ImportRoot,
@@ -30,14 +31,14 @@ fn resolve_import(
};
match &import.location_hashed.location {
Local(prefix, path) => {
- let path: PathBuf = path.iter().cloned().collect();
- let path = match prefix {
+ let path_buf: PathBuf = path.clone().into_iter().collect();
+ let path_buf = match prefix {
// TODO: fail gracefully
- Parent => cwd.parent().unwrap().join(path),
- Here => cwd.join(path),
+ Parent => cwd.parent().unwrap().join(path_buf),
+ Here => cwd.join(path_buf),
_ => unimplemented!("{:?}", import),
};
- Ok(load_import(&path, import_cache, import_stack).map_err(|e| {
+ Ok(load_import(&path_buf, import_cache, import_stack).map_err(|e| {
ImportError::Recursive(import.clone(), Box::new(e))
})?)
}
diff --git a/dhall_syntax/src/core/import.rs b/dhall_syntax/src/core/import.rs
index d41eae2..82bb7ff 100644
--- a/dhall_syntax/src/core/import.rs
+++ b/dhall_syntax/src/core/import.rs
@@ -11,10 +11,41 @@ pub enum FilePrefix {
Home,
}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Directory {
+ pub components: Vec<String>,
+}
+
+impl IntoIterator for Directory {
+ type Item = String;
+ type IntoIter = ::std::vec::IntoIter<Self::Item>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.components.into_iter()
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct File {
+ pub directory: Directory,
+ pub file: String,
+}
+
+impl IntoIterator for File {
+ type Item = String;
+ type IntoIter = ::std::vec::IntoIter<Self::Item>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ let mut paths = self.directory.components;
+ paths.push(self.file);
+ paths.into_iter()
+ }
+}
+
/// The location of import (i.e. local vs. remote vs. environment)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ImportLocation {
- Local(FilePrefix, Vec<String>),
+ Local(FilePrefix, File),
Remote(URL),
Env(String),
Missing,
@@ -24,7 +55,7 @@ pub enum ImportLocation {
pub struct URL {
pub scheme: Scheme,
pub authority: String,
- pub path: Vec<String>,
+ pub path: File,
pub query: Option<String>,
// TODO: implement inline headers
pub headers: Option<Box<ImportHashed>>,
@@ -61,3 +92,90 @@ pub struct Import {
pub mode: ImportMode,
pub location_hashed: ImportHashed,
}
+
+pub trait Canonicalize {
+ fn canonicalize(&self) -> Self;
+}
+
+impl Canonicalize for Directory {
+ fn canonicalize(&self) -> Directory {
+ let mut components = Vec::new();
+ let mut dir_components = self.clone().into_iter();
+
+ loop {
+ let component = dir_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 dir_components.next() {
+ // canonicalize(directory₀) = ε
+ // ────────────────────────────
+ // canonicalize(directory₀/..) = /..
+ None => components.push("..".to_string()),
+
+ // canonicalize(directory₀) = directory₁/..
+ // ──────────────────────────────────────────────
+ // canonicalize(directory₀/..) = directory₁/../..
+ Some(ref c) if c == ".." => {
+ components.push("..".to_string());
+ components.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) => components.push(c.clone()),
+ }
+ }
+
+ Directory { components: components }
+ }
+}
+
+impl Canonicalize for File {
+ fn canonicalize(&self) -> File {
+ File { directory: self.directory.canonicalize(), file: self.file.clone() }
+ }
+}
+
+impl Canonicalize for ImportLocation {
+ fn canonicalize(&self) -> ImportLocation {
+ 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().map(|boxed_hash| Box::new(boxed_hash.canonicalize())),
+ }),
+ ImportLocation::Env(name) => ImportLocation::Env(name.to_string()),
+ ImportLocation::Missing => ImportLocation::Missing,
+ }
+ }
+}
+
+impl Canonicalize for ImportHashed {
+ fn canonicalize(&self) -> ImportHashed {
+ ImportHashed { hash: self.hash.clone(), location: self.location.canonicalize() }
+ }
+}
+
+impl Canonicalize for Import {
+ fn canonicalize(&self) -> Import {
+ Import { mode: self.mode, location_hashed: self.location_hashed.canonicalize() }
+ }
+}
diff --git a/dhall_syntax/src/parser.rs b/dhall_syntax/src/parser.rs
index 9ae6dc8..a2495ee 100644
--- a/dhall_syntax/src/parser.rs
+++ b/dhall_syntax/src/parser.rs
@@ -147,6 +147,14 @@ fn debug_pair(pair: Pair<Rule>) -> String {
s
}
+fn to_file(path: Vec<String>) -> Result<File, String> {
+ let mut path = path;
+ let file_name: Option<String> = path.pop();
+ let file = file_name.ok_or("Empty file path was provided")?;
+ let directory = Directory { components: path };
+ Ok(File { directory: directory, file: file })
+}
+
macro_rules! make_parser {
(@pattern, rule, $name:ident) => (Rule::$name);
(@pattern, token_rule, $name:ident) => (Rule::$name);
@@ -630,19 +638,25 @@ make_parser! {
});
rule!(http_raw<URL>; children!(
- [scheme(sch), authority(auth), path(p)] => URL {
- scheme: sch,
- authority: auth,
- path: p,
- query: None,
- headers: None,
+ [scheme(sch), authority(auth), path(p)] => {
+ let file = to_file(p)?;
+ URL {
+ scheme: sch,
+ authority: auth,
+ path: file,
+ 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(p), query(q)] => {
+ let file = to_file(p)?;
+ URL {
+ scheme: sch,
+ authority: auth,
+ path: file,
+ query: Some(q),
+ headers: None,
+ }
},
));
@@ -696,7 +710,8 @@ make_parser! {
ImportLocation::Remote(url)
},
[local((prefix, p))] => {
- ImportLocation::Local(prefix, p)
+ let file = to_file(p)?;
+ ImportLocation::Local(prefix, file)
},
));
diff --git a/dhall_syntax/src/printer.rs b/dhall_syntax/src/printer.rs
index 70b224e..5312f23 100644
--- a/dhall_syntax/src/printer.rs
+++ b/dhall_syntax/src/printer.rs
@@ -365,17 +365,19 @@ impl Display for ImportHashed {
Absolute => "",
};
write!(f, "{}/", prefix)?;
- let path: String = path
- .iter()
+ let full_path: String = path
+ .clone()
+ .into_iter()
.map(|c| fmt_local_path_component(c.as_ref()))
.join("/");
- f.write_str(&path)?;
+ f.write_str(&full_path)?;
}
Remote(url) => {
write!(f, "{}://{}/", url.scheme, url.authority,)?;
let path: String = url
.path
- .iter()
+ .clone()
+ .into_iter()
.map(|c| fmt_remote_path_component(c.as_ref()))
.join("/");
f.write_str(&path)?;