diff --git a/README.md b/README.md index b7dc42bf..edda4f89 100644 --- a/README.md +++ b/README.md @@ -443,6 +443,21 @@ res0: org.dhallj.core.Expr = "foo-bar-baz" (Note that we could use dhall-scala to avoid the use of `Array` above.) +#### Classpath imports + +We support an extension of the spec which allows you to also import expressions +from the classpath using the syntax `let e = classpath:/absolute/path/to/file in e`. +The semantics are subject to change as we get more experience with it but +currently it should generally have the same behaviour as an absolute +path import of a local file (but files on the classpath can import each other +using relative paths). This includes it being protected by the referential +sanity check so that remote imports cannot exfiltrate information +from the classpath. + +Also note that classpath imports as location are currently not supported as the spec +requires that an import as Location must return an expression of type +``. + ### dhall-imports-mini The other implementation is dhall-imports-mini, which is a Java library that diff --git a/modules/imports/src/test/scala/org/dhallj/imports/ImportResolutionSuite.scala b/modules/imports/src/test/scala/org/dhallj/imports/ImportResolutionSuite.scala index ed9dbf11..2c543497 100644 --- a/modules/imports/src/test/scala/org/dhallj/imports/ImportResolutionSuite.scala +++ b/modules/imports/src/test/scala/org/dhallj/imports/ImportResolutionSuite.scala @@ -72,7 +72,7 @@ class ImportResolutionSuite extends FunSuite { assert(resolve(expr) == expected) } - test("Import as classpath location") { + test("Import as local location") { val expr = parse("let x = /foo/bar.dhall as Location in x") val expected = parse( diff --git a/modules/imports/src/test/scala/org/dhallj/imports/ReferentialSanityCheckSuite.scala b/modules/imports/src/test/scala/org/dhallj/imports/ReferentialSanityCheckSuite.scala index 35eaa592..9a5eb2d8 100644 --- a/modules/imports/src/test/scala/org/dhallj/imports/ReferentialSanityCheckSuite.scala +++ b/modules/imports/src/test/scala/org/dhallj/imports/ReferentialSanityCheckSuite.scala @@ -28,6 +28,10 @@ class ReferentialSanityCheckSuite extends FunSuite { ReferentialSanityCheck[IO](Remote(someUri, null), Missing).unsafeRunSync } + test("Remote imports classpath".fail) { + ReferentialSanityCheck[IO](Remote(someUri, null), Classpath(somePath)).unsafeRunSync + } + test("Local imports local") { ReferentialSanityCheck[IO](Local(somePath), Local(somePath)).unsafeRunSync } diff --git a/modules/parser/src/test/scala/org/dhallj/parser/DhallParserSuite.scala b/modules/parser/src/test/scala/org/dhallj/parser/DhallParserSuite.scala index 8566aeb0..a06e4d9e 100644 --- a/modules/parser/src/test/scala/org/dhallj/parser/DhallParserSuite.scala +++ b/modules/parser/src/test/scala/org/dhallj/parser/DhallParserSuite.scala @@ -1,8 +1,11 @@ package org.dhallj.parser import java.net.URI +import java.nio.file.Paths + import munit.{FunSuite, Ignore} import org.dhallj.core.Expr +import org.dhallj.core.Expr.ImportMode class DhallParserSuite extends FunSuite() { test("parse empty list with annotation on element type".tag(Ignore)) { @@ -30,4 +33,10 @@ class DhallParserSuite extends FunSuite() { assert(DhallParser.parse(""""# # # $ % ^ #"""") == expected) } + + test("parse classpath import") { + val expected = Expr.makeClasspathImport(Paths.get("/foo/bar.dhall"), ImportMode.RAW_TEXT, null) + + assert(DhallParser.parse("classpath:/foo/bar.dhall as Text") == expected) + } } diff --git a/tests/src/test/scala/org/dhallj/tests/BinaryDecodingTests.scala b/tests/src/test/scala/org/dhallj/tests/BinaryDecodingTests.scala index 6cc3f879..896d2d3f 100644 --- a/tests/src/test/scala/org/dhallj/tests/BinaryDecodingTests.scala +++ b/tests/src/test/scala/org/dhallj/tests/BinaryDecodingTests.scala @@ -298,6 +298,28 @@ class BinaryDecodingTests extends FunSuite { assert(decoded.equivalent(expected)) } + test("Decode classpath import") { + val bytes = parse("let x = classpath:/foo/bar.dhall in x").getEncodedBytes + + val decoded = decode(bytes) + val expected = parse( + "let x = classpath:/foo/bar.dhall in x" + ) + + assert(decoded.equivalent(expected)) + } + + test("Decode classpath import as text") { + val bytes = parse("let x = classpath:/foo/bar.dhall as Text in x").getEncodedBytes + + val decoded = decode(bytes) + val expected = parse( + "let x = classpath:/foo/bar.dhall as Text in x" + ) + + assert(decoded.equivalent(expected)) + } + private def load(resource: String): Array[Byte] = Files.readAllBytes(Paths.get(getClass.getResource(s"/binary/$resource").toURI))