diff options
| -rw-r--r-- | dhall/src/phase/.resolve.rs.swp | bin | 0 -> 16384 bytes | |||
| -rw-r--r-- | dhall/src/phase/binary.rs | 21 | ||||
| -rw-r--r-- | dhall/src/phase/resolve.rs | 11 | ||||
| -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 | 
6 files changed, 108 insertions, 28 deletions
| diff --git a/dhall/src/phase/.resolve.rs.swp b/dhall/src/phase/.resolve.rs.swpBinary files differ new file mode 100644 index 0000000..5314300 --- /dev/null +++ b/dhall/src/phase/.resolve.rs.swp diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs index 40219d9..16e7ce9 100644 --- a/dhall/src/phase/binary.rs +++ b/dhall/src/phase/binary.rs @@ -1,11 +1,12 @@  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, Expr, ExprF, FilePrefix, Hash, Import, ImportLocation, ImportMode, -    Integer, InterpolatedText, Label, Natural, Scheme, URL, V, +    Integer, InterpolatedText, Label, Natural, Scheme, URL, V, File  };  use crate::error::{DecodeError, EncodeError}; @@ -278,7 +279,7 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {                                  "import/remote/query".to_owned(),                              ))?,                          }; -                        let path = rest +                        let file_path: Vec<_> = rest                              .map(|s| match s.as_string() {                                  Some(s) => Ok(s.clone()),                                  None => Err(DecodeError::WrongFormatError( @@ -286,6 +287,7 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {                                  )),                              })                              .collect::<Result<_, _>>()?; +                        let path = File { file_path };                          ImportLocation::Remote(URL {                              scheme,                              authority, @@ -304,7 +306,7 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {                                  "import/local/prefix".to_owned(),                              ))?,                          }; -                        let path = rest +                        let file_path: Vec<_> = rest                              .map(|s| match s.as_string() {                                  Some(s) => Ok(s.clone()),                                  None => Err(DecodeError::WrongFormatError( @@ -312,6 +314,7 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {                                  )),                              })                              .collect::<Result<_, _>>()?; +                        let path = File { file_path };                          ImportLocation::Local(prefix, path)                      }                      6 => { @@ -584,8 +587,8 @@ where      use serde::ser::SerializeSeq;      let count = 4 + match &import.location { -        ImportLocation::Remote(url) => 3 + url.path.len(), -        ImportLocation::Local(_, path) => path.len(), +        ImportLocation::Remote(url) => 3 + url.path.file_path.len(), +        ImportLocation::Local(_, path) => path.file_path.len(),          ImportLocation::Env(_) => 1,          ImportLocation::Missing => 0,      }; @@ -635,8 +638,8 @@ where                  }              };              ser_seq.serialize_element(&url.authority)?; -            for p in &url.path { -                ser_seq.serialize_element(p)?; +            for p in url.path.file_path.iter() { +                ser_seq.serialize_element(&p)?;              }              match &url.query {                  None => ser_seq.serialize_element(&Null)?, @@ -644,8 +647,8 @@ where              };          }          ImportLocation::Local(_, path) => { -            for p in path { -                ser_seq.serialize_element(p)?; +            for p in path.file_path.iter() { +                ser_seq.serialize_element(&p)?;              }          }          ImportLocation::Env(env) => { diff --git a/dhall/src/phase/resolve.rs b/dhall/src/phase/resolve.rs index a58f5e4..5bde68a 100644 --- a/dhall/src/phase/resolve.rs +++ b/dhall/src/phase/resolve.rs @@ -16,6 +16,7 @@ type ImportCache = HashMap<Import, Normalized>;  pub(crate) type ImportStack = Vec<Import>; +  fn resolve_import(      import: &Import,      root: &ImportRoot, @@ -30,14 +31,14 @@ fn resolve_import(      };      match &import.location {          Local(prefix, path) => { -            let path: PathBuf = path.iter().cloned().collect(); -            let path = match prefix { +            let path_buf: PathBuf = path.file_path.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 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)? | 
