diff options
author | FintanH | 2019-08-12 22:22:24 +0100 |
---|---|---|
committer | FintanH | 2019-08-12 22:22:24 +0100 |
commit | 405bc3d80c0e169ea74dd12422b9504b7383dab3 (patch) | |
tree | f1f71797b0470375b12717a4663c1ea2ca1f96fd /dhall_syntax | |
parent | 8ec422f2319360f986950fcb9aae4bcf65a9c1e2 (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_syntax/src/core/import.rs | 122 | ||||
-rw-r--r-- | dhall_syntax/src/parser.rs | 41 | ||||
-rw-r--r-- | dhall_syntax/src/printer.rs | 10 |
3 files changed, 154 insertions, 19 deletions
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)?; |