Skip to content

Commit

Permalink
Ensure using expression has a valid type
Browse files Browse the repository at this point in the history
  • Loading branch information
travisbrown committed Jul 28, 2021
1 parent 624a37b commit e84a798
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ final private class ResolveImportsVisitor[F[_] <: AnyRef](
} yield v
case ImportContext.Remote(uri, using) =>
for {
headers <- F.pure(ToHeaders(`using`))
headers <- F.fromOption(ToHeaders(`using`), new ResolutionFailure("Invalid using clause"))
req <- F.pure(Request[F](uri = unsafeFromString(uri.toString), headers = headers))
resp <- Client.fetch[String](req) {
case Successful(resp) =>
Expand Down
97 changes: 66 additions & 31 deletions modules/imports/src/main/scala/org/dhallj/imports/ToHeaders.scala
Original file line number Diff line number Diff line change
@@ -1,54 +1,89 @@
package org.dhallj.imports

import java.util.AbstractMap.SimpleImmutableEntry
import java.util.Map.Entry
import org.dhallj.core.Expr
import org.dhallj.core.Expr.Util.{asListLiteral, asRecordLiteral, asSimpleTextLiteral}
import org.dhallj.core.typechecking.TypeCheckFailure
import org.http4s.{Header, Headers}
import org.typelevel.ci.CIString

import scala.collection.JavaConverters._

object ToHeaders {
private val validType1: Expr = Expr.makeApplication(
Expr.Constants.LIST,
Expr.makeRecordType(
Array[Entry[String, Expr]](
new SimpleImmutableEntry("mapKey", Expr.Constants.TEXT),
new SimpleImmutableEntry("mapValue", Expr.Constants.TEXT)
)
)
)

private val validType2: Expr = Expr.makeApplication(
Expr.Constants.LIST,
Expr.makeRecordType(
Array[Entry[String, Expr]](
new SimpleImmutableEntry("header", Expr.Constants.TEXT),
new SimpleImmutableEntry("value", Expr.Constants.TEXT)
)
)
)

private def isValidType(expr: Expr): Boolean = {
try {
val tpe = Expr.Util.typeCheck(expr)

tpe == validType1 || tpe == validType2
} catch {
case _: TypeCheckFailure => false
}
}

// See https://discourse.dhall-lang.org/t/valid-expressions-for-using-headers/205
// For the moment, this is consistent with the Haskell implementation
def apply(expr: Expr): Headers =
if (expr eq null) Headers.empty
def apply(expr: Expr): Option[Headers] =
if (expr eq null) Some(Headers.empty)
else {
//TODO do we need to .accept(this) on expr?
val e = expr.normalize
val l = asListLiteral(e)
if (l eq null) {
Headers.empty
if (!isValidType(expr)) {
None
} else {
val hs: List[Header.Raw] = l.asScala.toList.flatMap { e =>
// e should have type `List { header : Text, value Text }`
// or `List { mapKey : Text, mapValue Text }`
val r = asRecordLiteral(e)
if (r eq null) {
None
} else {
if (r.size == 2) {
val map = r.asScala.map(e => e.getKey -> e.getValue).toMap
if (map.contains("header") && map.contains("value")) {
val key = asSimpleTextLiteral(map("header"))
val value = asSimpleTextLiteral(map("value"))

if ((key ne null) && (value ne null)) {
Some(Header.Raw(CIString(key), value))
} else None
} else if (map.contains("mapKey") && map.contains("mapValue")) {
val key = asSimpleTextLiteral(map("mapKey"))
val value = asSimpleTextLiteral(map("mapValue"))
//TODO do we need to .accept(this) on expr?
val e = expr.normalize
val l = asListLiteral(e)
if (l eq null) {
Some(Headers.empty)
} else {
val hs: List[Header.Raw] = l.asScala.toList.flatMap { e =>
// e should have type `List { header : Text, value Text }`
// or `List { mapKey : Text, mapValue Text }`
val r = asRecordLiteral(e)
if (r eq null) {
None
} else {
if (r.size == 2) {
val map = r.asScala.map(e => e.getKey -> e.getValue).toMap
if (map.contains("header") && map.contains("value")) {
val key = asSimpleTextLiteral(map("header"))
val value = asSimpleTextLiteral(map("value"))

if ((key ne null) && (value ne null)) {
Some(Header.Raw(CIString(key), value))
} else None
} else if (map.contains("mapKey") && map.contains("mapValue")) {
val key = asSimpleTextLiteral(map("mapKey"))
val value = asSimpleTextLiteral(map("mapValue"))

if ((key ne null) && (value ne null)) {
Some(Header.Raw(CIString(key), value))
if ((key ne null) && (value ne null)) {
Some(Header.Raw(CIString(key), value))
} else None
} else None
} else None
} else None
}
}
Some(Headers(hs))
}
Headers(hs)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ToHeadersSuite extends FunSuite {
)
)

assertEquals(ToHeaders(expr), expected)
assertEquals(ToHeaders(expr), Some(expected))
}

test("Success case 2") {
Expand All @@ -50,7 +50,7 @@ class ToHeadersSuite extends FunSuite {
)
)

assertEquals(ToHeaders(expr), expected)
assertEquals(ToHeaders(expr), Some(expected))
}

test("Failure case 1") {
Expand All @@ -65,17 +65,13 @@ class ToHeadersSuite extends FunSuite {
)
)

val expected = Headers(Nil)

assertEquals(ToHeaders(expr), expected)
assertEquals(ToHeaders(expr), None)
}

test("Failure case 2") {
val expr = Expr.makeTextLiteral("foo")

val expected = Headers(Nil)

assertEquals(ToHeaders(expr), expected)
assertEquals(ToHeaders(expr), None)
}

}

0 comments on commit e84a798

Please sign in to comment.