Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle local imports with quoted paths #35

Merged
merged 1 commit into from
Apr 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,12 @@ import org.dhallj.imports.ResolveImportsVisitor._

import scala.collection.JavaConverters._

/**
* Abstract over java.nio.Path so we can assert that their
* normalization has the same behaviour as the Dhall spec's
* import path canonicalization
*
* TODO check behaviour of `canonicalize /..` as the Dhall
* spec seems to differ from Java here
*/
object Canonicalization {

def canonicalize[F[_]](imp: ImportContext)(implicit F: Sync[F]): F[ImportContext] = imp match {
case Remote(uri, headers) => F.delay(Remote(uri.normalize, headers))
case Local(path) => LocalFile[F](path).map(_.canonicalize.toPath).map(Local)
case Classpath(path) => F.delay(Classpath(path.normalize))
case Classpath(path) => LocalFile[F](path).map(_.canonicalize.toPath).map(Classpath)
case i => F.pure(i)
}

Expand Down Expand Up @@ -55,8 +47,10 @@ object Canonicalization {
child match {
//Also note that if the path is absolute then this violates referential sanity but we handle that elsewhere
case Local(path2) =>
if (path2.isAbsolute) canonicalize(Classpath(path2))
else canonicalize(Classpath(path.getParent.resolve(path2)))
for {
parent <- LocalFile[F](path)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a big deal, but what do you think about using F.map2 instead of for-comprehensions in cases like this where there's not a data dependency? It's a little more correct, and in my view it also reads a bit more nicely.

c <- LocalFile[F](path2)
} yield Classpath(parent.chain(c).canonicalize.toPath)
case _ => canonicalize(child)
}
case _ => canonicalize(child)
Expand Down Expand Up @@ -89,7 +83,8 @@ object Canonicalization {
case l => F.pure(LocalFile(LocalDirs(l.take(l.length - 1)), l.last))
}

def canonicalize(f: LocalFile): LocalFile = LocalFile(f.dirs.canonicalize, f.filename)
def canonicalize(f: LocalFile): LocalFile =
LocalFile(f.dirs.canonicalize, f.filename.stripPrefix("\"").stripSuffix("\""))

def chain(lhs: LocalFile, rhs: LocalFile): LocalFile = LocalFile(LocalDirs.chain(lhs.dirs, rhs.dirs), rhs.filename)
}
Expand All @@ -107,7 +102,7 @@ object Canonicalization {

def canonicalize(d: LocalDirs): LocalDirs = d.ds match {
case Nil => d
case l => LocalDirs(canonicalize(l))
case l => LocalDirs(canonicalize(l.map(_.stripPrefix("\"").stripSuffix("\""))))
}

def canonicalize(l: List[String]): List[String] =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package org.dhallj.imports

import java.math.BigInteger
import java.net.URI
import java.nio.file.Path
import java.security.MessageDigest
import java.util.{List => JList, Map => JMap}

import cats.effect.Sync
import cats.implicits._
import org.dhallj.cats.LiftVisitor
import org.dhallj.core._
import org.dhallj.core.DhallException.ResolutionFailure
import org.dhallj.core.Expr.ImportMode
import org.dhallj.core.Expr.Util.typeCheck
import org.dhallj.core._
import org.dhallj.core.binary.Decode
import org.dhallj.imports.Caching.ImportsCache
import org.dhallj.imports.Canonicalization.canonicalize
Expand All @@ -25,7 +23,6 @@ import org.http4s.{EntityDecoder, Headers, Request}

import scala.collection.mutable.{Map => MMap}

//TODO quoted path components?
//TODO proper error handling
private[dhallj] case class ResolveImportsVisitor[F[_] <: AnyRef](cache: ImportsCache[F], parents: List[ImportContext])(
implicit Client: Client[F],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ class CanonicalizationSuite extends FunSuite {
assertEquals(canonicalize[IO](Local(Paths.get("/foo.dhall"))).unsafeRunSync, Local(Paths.get("/foo.dhall")))
}

test("Imports - quoted") {
assertEquals(canonicalize[IO](Local(Paths.get("/\"foo\"/\"bar.dhall\""))).unsafeRunSync,
Local(Paths.get("/foo/bar.dhall")))
}

test("Paths - Trailing .") {
assertEquals(canonicalize[IO](Local(Paths.get("/foo/./bar.dhall"))).unsafeRunSync,
Local(Paths.get("/foo/bar.dhall")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ class ImportResolutionSuite extends FunSuite {
assert(resolve(expr) == expected)
}

test("Quoted import") {
val expr = parse("let x = classpath:/\"local\"/\"package.dhall\" in x")
val expected = parse("let x = 1 in x").normalize

assert(resolve(expr) == expected)
}

test("Classpath -> classpath relative import") {
val expr = parse("let x = classpath:/local-local-relative/package.dhall in x")
val expected = parse("let x = 1 in x").normalize
Expand Down Expand Up @@ -58,6 +65,16 @@ class ImportResolutionSuite extends FunSuite {
assert(resolve(expr) == expected)
}

//TODO - dependent on https://github.com/travisbrown/dhallj/issues/34
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can merge this for now as it is here. I'll look into failing more reasonably in the parser on quoted URL paths this weekend.

test("Quoted remote import".fail) {
val expr = parse(
"let any = https://raw.githubusercontent.com/\"dhall-lang\"/\"dhall-lang\"/\"master\"/\"Prelude\"/\"List\"/\"any\" in any Natural Natural/even [2,3,5]"
)
val expected = parse("True").normalize

assert(resolve(expr) == expected)
}

test("Multiple imports") {
val expr = parse("let x = classpath:/multiple-imports/package.dhall in x")
val expected = parse("let x = [1,2] in x").normalize
Expand Down