summaryrefslogtreecommitdiff
path: root/dhall
diff options
context:
space:
mode:
authorFintanH2019-08-12 22:22:24 +0100
committerFintanH2019-08-12 22:22:24 +0100
commit405bc3d80c0e169ea74dd12422b9504b7383dab3 (patch)
treef1f71797b0470375b12717a4663c1ea2ca1f96fd /dhall
parent8ec422f2319360f986950fcb9aae4bcf65a9c1e2 (diff)
Refactor of File to be the combination of Directory and the file name,
where Directory is the Vector of component paths. The refactor meant changing some sections of the code where we were parsing and manipulating Files/Directories. This also includes a new trait Canonicalization which is needed for import logic.
Diffstat (limited to '')
-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 1812131..3acd2d4 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};
@@ -243,7 +244,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(
@@ -251,6 +252,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,
@@ -269,7 +275,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(
@@ -277,6 +283,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 => {
@@ -520,8 +531,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,
};
@@ -573,8 +584,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)?,
@@ -582,8 +593,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 c4d7e5f..ba75be4 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 c328e34..ea42dbc 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>,
pub headers: Option<Box<ImportHashed>>,
}
@@ -59,3 +90,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 9d9a374..7675622 100644
--- a/dhall_syntax/src/parser.rs
+++ b/dhall_syntax/src/parser.rs
@@ -146,6 +146,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);
@@ -589,19 +597,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,
+ }
},
));
@@ -655,7 +669,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 dbed55d..8503a1e 100644
--- a/dhall_syntax/src/printer.rs
+++ b/dhall_syntax/src/printer.rs
@@ -380,17 +380,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)?;