com.twitter.util.Return Scala Examples

The following examples show how to use com.twitter.util.Return. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: arbitrary.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util

import com.twitter.concurrent.AsyncStream
import com.twitter.conversions.DurationOps._
import com.twitter.util.{ Future, Return, Try, Var }
import org.scalacheck.{ Arbitrary, Cogen }

trait ArbitraryInstances {
  implicit def futureArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Future[A]] =
    Arbitrary(A.arbitrary.map(Future.value))

  implicit def tryArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Try[A]] =
    Arbitrary(A.arbitrary.map(Return(_)))

  implicit def varArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Var[A]] =
    Arbitrary(A.arbitrary.map(Var.value))

  implicit def asyncStreamArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[AsyncStream[A]] =
    Arbitrary(A.arbitrary.map(AsyncStream.of))

  implicit def rerunnableArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Rerunnable[A]] =
    Arbitrary(futureArbitrary[A].arbitrary.map(Rerunnable.fromFuture[A](_)))

  implicit def cogenFuture[A](implicit A: Cogen[A]): Cogen[Future[A]] =
    A.contramap(futureComonad(1.second).extract)

  implicit def cogenVar[A](implicit A: Cogen[A]): Cogen[Var[A]] =
    A.contramap(varComonad.extract)

  implicit def cogenRerunnable[A](implicit A: Cogen[A]): Cogen[Rerunnable[A]] =
    A.contramap(Rerunnable.rerunnableComonad(1.second).extract)
} 
Example 2
Source File: DcosReleaseVersionParser.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.universe.v3.model

import com.twitter.util.{Return, Throw, Try}

import java.util.regex.Pattern

object DcosReleaseVersionParser {

  private[this] val versionFragment = "(?:0|[1-9][0-9]*)"
  private[this] val subVersionFragment = "\\." + versionFragment
  private[this] val suffixFragment =
    "((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)"

  private[v3] val versionRegex = s"^$versionFragment$$"
  private[v3] val suffixRegex = s"^$suffixFragment$$"
  private[v3] val fullRegex = s"^$versionFragment(?:$subVersionFragment)*(?<suffix>-$suffixFragment)?$$"

  private[v3] val versionPattern = Pattern.compile(versionRegex)
  private[v3] val suffixPattern = Pattern.compile(suffixRegex)
  private[v3] val fullPattern = Pattern.compile(fullRegex)

  def parseUnsafe(s: String): DcosReleaseVersion = parse(s).get

  def parse(s: String): Try[DcosReleaseVersion] = {
    val errMsg = s"Value '$s' does not conform to expected format $fullRegex"
    Try {
      assert(!s.trim.isEmpty, "Value must not be empty")
      assert(fullPattern.matcher(s).matches(), errMsg)
      s
    } flatMap { validatedString =>
      validatedString.split('-').toList match {
        case Seq(version) =>
          Return(version -> None)
        case Seq(version, tail @ _*) =>
          Return(version -> Some(tail.mkString("-")))
        case _ =>
          Throw(new AssertionError(errMsg))
      }
    } flatMap { case (version, subVersion) =>
      parseVersionSuffix(version, subVersion, errMsg)
    }
  }

  private[this] def parseVersion(s: String): Try[DcosReleaseVersion.Version] = Try {
    DcosReleaseVersion.Version(s.toInt)
  }

  private[this] def parseSuffix(s: Option[String]): Try[Option[DcosReleaseVersion.Suffix]] = s match {
    case None => Return(None)
    case Some(suff) => Return(Some(DcosReleaseVersion.Suffix(suff)))
  }

  private[this] def parseVersionSuffix(version: String, suffix: Option[String], errMsg: String): Try[DcosReleaseVersion] = {
    version.split('.').toList match {
      case head :: tail :: Nil =>
        for {
          h <- parseVersion(head)
          t <- parseVersion(tail)
          s <- parseSuffix(suffix)
        } yield {
          DcosReleaseVersion(h, List(t), s)
        }
      case head :: Nil =>
        for {
          h <- parseVersion(head)
          s <- parseSuffix(suffix)
        } yield {
          DcosReleaseVersion(h, List.empty, s)
        }
      case head :: tail =>
        for {
          h <- parseVersion(head)
          t <- Try.collect(tail.map(parseVersion))
          s <- parseSuffix(suffix)
        } yield {
          DcosReleaseVersion(h, t.toList, s)
        }
      case _ =>
        Throw(new AssertionError(errMsg))
    }
  }

} 
Example 3
Source File: Tag.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.universe.v3.model

import com.mesosphere.cosmos.circe.Decoders._
import com.twitter.util.Return
import com.twitter.util.Throw
import com.twitter.util.Try
import io.circe.syntax.EncoderOps
import io.circe.Decoder
import io.circe.DecodingFailure
import io.circe.Encoder
import io.circe.HCursor
import java.util.regex.Pattern

final class Tag private(val value: String) extends AnyVal {

  override def toString: String = value

}

object Tag {

  val packageDetailsTagRegex: String = "^[^\\s]+$"
  val packageDetailsTagPattern: Pattern = Pattern.compile(packageDetailsTagRegex)

  def apply(s: String): Tag = validate(s).get

  def validate(s: String): Try[Tag] = {
    if (packageDetailsTagPattern.matcher(s).matches()) {
      Return(new Tag(s))
    } else {
      Throw(new IllegalArgumentException(
        s"Value '$s' does not conform to expected format $packageDetailsTagRegex"
      ))
    }
  }

  implicit val encodePackageDefinitionTag: Encoder[Tag] = {
    Encoder.instance(_.value.asJson)
  }

  implicit val decodePackageDefinitionTag: Decoder[Tag] =
    Decoder.instance[Tag] { (c: HCursor) =>
      c.as[String].map(validate(_)).flatMap {
        case Return(r) => Right(r)
        case Throw(ex) =>
          val msg = ex.getMessage.replaceAllLiterally("assertion failed: ", "")
          Left(DecodingFailure(msg, c.history))
      }
    }

} 
Example 4
Source File: ReleaseVersion.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.universe.v3.model

import com.twitter.util.Return
import com.twitter.util.Throw
import com.twitter.util.Try
import io.circe.Decoder
import io.circe.DecodingFailure
import io.circe.Encoder
import io.circe.HCursor
import io.circe.syntax.EncoderOps

final class ReleaseVersion private(val value: Long) extends AnyVal

object ReleaseVersion {

  def apply(value: Long): ReleaseVersion = validate(value).get

  def validate(value: Long): Try[ReleaseVersion] = {
    if (value >= 0) {
      Return(new ReleaseVersion(value))
    } else {
      val message = s"Expected integer value >= 0 for release version, but found [$value]"
      Throw(new IllegalArgumentException(message))
    }
  }

  implicit val packageDefinitionReleaseVersionOrdering: Ordering[ReleaseVersion] = {
    Ordering.by(_.value)
  }

  implicit val encodePackageDefinitionReleaseVersion: Encoder[ReleaseVersion] = {
    Encoder.instance(_.value.asJson)
  }

  implicit val decodePackageDefinitionReleaseVersion: Decoder[ReleaseVersion] =
    Decoder.instance[ReleaseVersion] { (c: HCursor) =>
      c.as[Long].map(validate).flatMap {
        case Return(v) => Right(v)
        case Throw(e) => Left(DecodingFailure(e.getMessage, c.history))
      }
    }


} 
Example 5
Source File: ServicesIntegrationSpec.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos

import io.lemonlabs.uri.dsl._
import com.twitter.conversions.storage._
import com.twitter.finagle.http.RequestBuilder
import com.twitter.finagle.http.Status
import com.twitter.util.Await
import com.twitter.util.Return
import org.scalatest.FreeSpec

final class ServicesIntegrationSpec extends FreeSpec {

  "Services" - {
    "adminRouterClient should" - {
      "be able to connect to an https site" in {
        val url = "https://www.google.com"
        val Return(client) = Services.adminRouterClient(url, 5.megabytes)

        val request = RequestBuilder().url(url).buildGet()
        val response = Await.result(client(request))
        assertResult(response.status)(Status.Ok)
      }
    }
  }
} 
Example 6
Source File: DefaultRepositoriesSpec.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.repository

import com.mesosphere.cosmos.repository.DefaultRepositories._
import com.mesosphere.cosmos.rpc.v1.model.PackageRepository
import io.lemonlabs.uri.dsl._
import com.twitter.util.Return
import io.circe.ParsingFailure
import org.scalatest.FreeSpec

class DefaultRepositoriesSpec extends FreeSpec {

  val repo1 = PackageRepository("repo1", "http://someplace/repo1")
  val repo2 = PackageRepository("repo2", "http://someplace/repo2")
  val repo3 = PackageRepository("repo3", "http://someplace/repo3")

  "DefaultRepositories for" - {

    "empty-list.json should" - {
      val emptyList = new DefaultRepositories("/com/mesosphere/cosmos/repository/empty-list.json")

      "property load" in {
        val expected = List()
        assertResult(expected)(emptyList.getOrThrow)
      }

    }

    "repos.json should" - {
      val repos = new DefaultRepositories("/com/mesosphere/cosmos/repository/repos.json")

      "property load" in {
        val expected = List(repo1, repo2, repo3)
        assertResult(expected)(repos.getOrThrow)
      }

    }

    "malformed.json should" - {
      val malformed = new DefaultRepositories("/com/mesosphere/cosmos/repository/malformed.json")

      "collect an error" in {
        val Return(xor) = malformed.get()
        assert(xor.isLeft)
      }

      "throw an error for a getOrThrow()" in {
        try { val _ = malformed.getOrThrow }
        catch {
          case ParsingFailure(_, _) => // expected
        }
      }

      "return a default for a getOrElse()" in {
        val actual = malformed.getOrElse(List(repo1))
        val expected = List(repo1)
        assertResult(expected)(actual)
      }

    }

    "non-existent resource should" - {

      "throw IllegalStateException" in {
        try {
          val _ = new DefaultRepositories("/does/not/exist").getOrThrow
        } catch {
          case _: IllegalStateException => // expected
        }
      }

    }

    "default-repositories.json should load" in {
      val defaults = DefaultRepositories().getOrThrow
      assertResult(List(PackageRepository("default", "http://default")))(defaults)
    }

  }

} 
Example 7
Source File: TrysSpec.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos

import org.scalatest.FreeSpec
import com.twitter.util.Return
import com.twitter.util.Throw

final class TrysSpec extends FreeSpec {

  "join[A,B]" in {
    assertResult(Return((1,2)))(Trys.join(Return(1), Return(2)))
    val e = new IllegalArgumentException
    val n = new NoSuchElementException
    assertResult(Throw(e))(Trys.join(Throw(e), Return(2)))
    assertResult(Throw(e))(Trys.join(Return(1), Throw(e)))
    assertResult(Throw(n))(Trys.join(Throw(n), Throw(e)))
  }

  "join[A,B,C]" in {
    assertResult(Return((1,2,3)))(Trys.join(Return(1), Return(2), Return(3)))
    val e = new IllegalArgumentException
    val n = new NoSuchElementException
    assertResult(Throw(e))(Trys.join(Throw(e), Return(2), Return(3)))
    assertResult(Throw(e))(Trys.join(Return(1), Throw(e), Return(3)))
    assertResult(Throw(n))(Trys.join(Throw(n), Throw(e), Return(3)))
  }
} 
Example 8
Source File: UrisSpec.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos

import io.lemonlabs.uri.dsl._
import com.twitter.util.{Return, Throw}
import org.scalatest.FreeSpec

class UrisSpec extends FreeSpec {

  "extractHostAndPort should" - {
    "succeed for" - {
      val http = 80
      val https = 443
      val httpAlt = 8080

      "http://domain" in {
        assertResult(Return(ConnectionDetails("domain", http, tls = false)))(Uris.extractHostAndPort("http://domain"))
      }
      "https://domain" in {
        assertResult(Return(ConnectionDetails("domain", https, tls = true)))(Uris.extractHostAndPort("https://domain"))
      }
      "http://domain:8080" in {
        assertResult(Return(ConnectionDetails("domain", httpAlt, tls = false)))(Uris.extractHostAndPort("http://domain:8080"))
      }
      "http://sub.domain" in {
        assertResult(Return(ConnectionDetails("sub.domain", http, tls = false)))(Uris.extractHostAndPort("http://sub.domain"))
      }
      "https://sub.domain" in {
        assertResult(Return(ConnectionDetails("sub.domain", https, tls = true)))(Uris.extractHostAndPort("https://sub.domain"))
      }
      "http://10.0.0.1" in {
        assertResult(Return(ConnectionDetails("10.0.0.1", http, tls = false)))(Uris.extractHostAndPort("http://10.0.0.1"))
      }
      "https://10.0.0.1" in {
        assertResult(Return(ConnectionDetails("10.0.0.1", https, tls = true)))(Uris.extractHostAndPort("https://10.0.0.1"))
      }
    }
    "fail for" - {
      "domain" in {
        val Throw(err) = Uris.extractHostAndPort("domain")
        val expectedMessage = "Unsupported or invalid URI. Expected format 'http[s]://example.com[:port][/base-path]' actual 'domain'"
        assertResult(expectedMessage)(err.getMessage)
      }
      "domain:8080" in {
        val Throw(err) = Uris.extractHostAndPort("domain:8080")
        val expectedMessage = "Unsupported or invalid URI. Expected format 'http[s]://example.com[:port][/base-path]' actual 'domain:8080'"
        assertResult(expectedMessage)(err.getMessage)
      }
      "ftp://domain" in {
        val Throw(err) = Uris.extractHostAndPort("ftp://domain")
        val expectedMessage = "Unsupported or invalid URI. Expected format 'http[s]://example.com[:port][/base-path]' actual 'ftp://domain'"
        assertResult(expectedMessage)(err.getMessage)
      }
    }
  }

} 
Example 9
Source File: ServicesSpec.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos

import com.mesosphere.cosmos.error.CosmosException
import com.mesosphere.cosmos.error.ServiceUnavailable
import io.lemonlabs.uri.dsl._
import com.twitter.conversions.storage._
import com.twitter.finagle.http.RequestBuilder
import com.twitter.util.Await
import com.twitter.util.Return
import io.netty.handler.codec.http.HttpResponseStatus
import org.scalatest.FreeSpec

final class ServicesSpec extends FreeSpec {

  "Services" - {

    "adminRouterClient should" - {
      "be able to be created with a well formed URI with a domain that doesn't resolve" in {
        val url = "http://some.domain.that-im-pretty-sure-doesnt-exist-anywhere"
        val Return(client) = Services.adminRouterClient(url, 5.megabytes)

        try {
          val request = RequestBuilder().url(url).buildGet()
          Await.result(client(request))
        } catch {
          case e: CosmosException if e.error.isInstanceOf[ServiceUnavailable] =>
            assertResult(HttpResponseStatus.SERVICE_UNAVAILABLE)(e.error.status)
        } finally {
          val _ = Await.ready(client.close())
        }
      }
    }

  }
} 
Example 10
Source File: ResponseSpec.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.converter

import com.mesosphere.cosmos.converter.Response._
import com.mesosphere.cosmos.error.ServiceMarathonTemplateNotFound
import com.mesosphere.cosmos.rpc
import com.mesosphere.cosmos.thirdparty.marathon.model.AppId
import com.mesosphere.universe
import com.mesosphere.universe.bijection.UniverseConversions._
import com.mesosphere.universe.v3.model.Cli
import com.twitter.bijection.Conversion.asMethod
import com.twitter.util.Return
import com.twitter.util.Throw
import com.twitter.util.Try
import org.scalacheck.Gen
import org.scalatest.FreeSpec
import org.scalatest.Matchers
import org.scalatest.prop.GeneratorDrivenPropertyChecks.forAll

final class ResponseSpec extends FreeSpec with Matchers {
  "Conversion[rpc.v2.model.InstallResponse,Try[rpc.v1.model.InstallResponse]]" - {
    val vstring = "9.87.654.3210"
    val ver = universe.v3.model.Version(vstring)
    val name = "ResponseSpec"
    val appid = AppId("foobar")
    val clis = List(None, Some("post install notes"))
    val notes = List(None, Some(Cli(None)))
    val validV2s = for {
      n <- Gen.oneOf(clis)
      c <- Gen.oneOf(notes)
    } yield (rpc.v2.model.InstallResponse(name, ver, Some(appid), n, c))
    val invalidV2s = for {
      n <- Gen.oneOf(clis)
      c <- Gen.oneOf(notes)
    } yield (rpc.v2.model.InstallResponse(name, ver, None, n, c))


    "success" in {
      val v1 = rpc.v1.model.InstallResponse(name, ver.as[universe.v2.model.PackageDetailsVersion], appid)

      forAll(validV2s) { x =>
        x.as[Try[rpc.v1.model.InstallResponse]] shouldBe Return(v1)
      }
    }
    "failure" in {
      //expecting failure due to missing marathon mustache
      forAll(invalidV2s) { x =>
        x.as[Try[rpc.v1.model.InstallResponse]] shouldBe Throw(
          ServiceMarathonTemplateNotFound(name, ver).exception
        )
      }
    }
  }

} 
Example 11
Source File: FinaglePostgresDecoders.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.postgres

import java.nio.charset.Charset
import java.time.{ LocalDate, LocalDateTime, ZoneId }
import java.util.{ Date, UUID }

import com.twitter.finagle.postgres.values.ValueDecoder
import com.twitter.util.Return
import com.twitter.util.Throw
import com.twitter.util.Try
import io.getquill.FinaglePostgresContext
import io.getquill.util.Messages.fail
import io.netty.buffer.ByteBuf

trait FinaglePostgresDecoders {
  this: FinaglePostgresContext[_] =>

  import ValueDecoder._

  type Decoder[T] = FinaglePostgresDecoder[T]

  case class FinaglePostgresDecoder[T](
    vd:      ValueDecoder[T],
    default: Throwable => T  = (e: Throwable) => fail(e.getMessage)
  ) extends BaseDecoder[T] {
    override def apply(index: Index, row: ResultRow): T =
      row.getTry[T](index)(vd) match {
        case Return(r) => r
        case Throw(e)  => default(e)
      }

    def orElse[U](f: U => T)(implicit vdu: ValueDecoder[U]): FinaglePostgresDecoder[T] = {
      val mappedVd = vdu.map[T](f)
      FinaglePostgresDecoder[T](
        new ValueDecoder[T] {
          def decodeText(recv: String, text: String): Try[T] = {
            val t = vd.decodeText(recv, text)
            if (t.isReturn) t
            else mappedVd.decodeText(recv, text)
          }
          def decodeBinary(recv: String, bytes: ByteBuf, charset: Charset): Try[T] = {
            val t = vd.decodeBinary(recv, bytes, charset)
            if (t.isReturn) t
            else mappedVd.decodeBinary(recv, bytes, charset)
          }
        }
      )
    }
  }

  implicit def decoderDirectly[T](implicit vd: ValueDecoder[T]): Decoder[T] = FinaglePostgresDecoder(vd)
  def decoderMapped[U, T](f: U => T)(implicit vd: ValueDecoder[U]): Decoder[T] = FinaglePostgresDecoder(vd.map[T](f))

  implicit def optionDecoder[T](implicit d: Decoder[T]): Decoder[Option[T]] =
    FinaglePostgresDecoder[Option[T]](
      new ValueDecoder[Option[T]] {
        def decodeText(recv: String, text: String): Try[Option[T]] = Return(d.vd.decodeText(recv, text).toOption)
        def decodeBinary(recv: String, bytes: ByteBuf, charset: Charset): Try[Option[T]] = Return(d.vd.decodeBinary(recv, bytes, charset).toOption)
      },
      _ => None
    )

  implicit def mappedDecoder[I, O](implicit mapped: MappedEncoding[I, O], d: Decoder[I]): Decoder[O] =
    decoderMapped[I, O](mapped.f)(d.vd)

  implicit val stringDecoder: Decoder[String] = decoderDirectly[String]
  implicit val bigDecimalDecoder: Decoder[BigDecimal] = decoderDirectly[BigDecimal]
  implicit val booleanDecoder: Decoder[Boolean] = decoderDirectly[Boolean]
  implicit val shortDecoder: Decoder[Short] = decoderDirectly[Short]
  implicit val byteDecoder: Decoder[Byte] = decoderMapped[Short, Byte](_.toByte)
  implicit val intDecoder: Decoder[Int] = decoderDirectly[Int].orElse[Long](_.toInt)
  implicit val longDecoder: Decoder[Long] = decoderDirectly[Long].orElse[Int](_.toLong)
  implicit val floatDecoder: Decoder[Float] = decoderDirectly[Float].orElse[Double](_.toFloat)
  implicit val doubleDecoder: Decoder[Double] = decoderDirectly[Double]
  implicit val byteArrayDecoder: Decoder[Array[Byte]] = decoderDirectly[Array[Byte]]
  implicit val dateDecoder: Decoder[Date] = decoderMapped[LocalDateTime, Date](d => Date.from(d.atZone(ZoneId.systemDefault()).toInstant))
  implicit val localDateDecoder: Decoder[LocalDate] = decoderDirectly[LocalDate].orElse[LocalDateTime](_.toLocalDate)
  implicit val localDateTimeDecoder: Decoder[LocalDateTime] = decoderDirectly[LocalDateTime].orElse[LocalDate](_.atStartOfDay)
  implicit val uuidDecoder: Decoder[UUID] = decoderDirectly[UUID]
} 
Example 12
Source File: TwitterFutureIOMonad.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.monad

import language.experimental.macros
import com.twitter.util.Future
import io.getquill.context.Context
import com.twitter.util.Return
import scala.util.Success
import com.twitter.util.Throw
import scala.util.Failure
import com.twitter.util.Try
import io.getquill.{ Query, Action, ActionReturning, BatchAction }

trait TwitterFutureIOMonad extends IOMonad {
  this: Context[_, _] =>

  type Result[T] = Future[T]

  def runIO[T](quoted: Quoted[T]): IO[RunQuerySingleResult[T], Effect.Read] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[Query[T]]): IO[RunQueryResult[T], Effect.Read] = macro IOMonadMacro.runIO
  def runIO(quoted: Quoted[Action[_]]): IO[RunActionResult, Effect.Write] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[ActionReturning[_, T]]): IO[RunActionReturningResult[T], Effect.Write] = macro IOMonadMacro.runIO
  def runIO(quoted: Quoted[BatchAction[Action[_]]]): IO[RunBatchActionResult, Effect.Write] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[BatchAction[ActionReturning[_, T]]]): IO[RunBatchActionReturningResult[T], Effect.Write] = macro IOMonadMacro.runIO

  case class Run[T, E <: Effect](f: () => Result[T]) extends IO[T, E]

  def performIO[T](io: IO[T, _], transactional: Boolean = false): Result[T] =
    io match {
      case FromTry(t) => Future.const(Try(t.get))
      case Run(f)     => f()
      case Sequence(in, cbf) =>
        Future.collect(in.map(performIO(_)).toSeq)
          .map(r => cbf().++=(r).result)
      case TransformWith(a, fA) =>
        performIO(a)
          .liftToTry.map {
            case Return(v) => Success(v)
            case Throw(t)  => Failure(t)
          }
          .flatMap(v => performIO(fA(v)))
      case Transactional(io) =>
        performIO(io, transactional = true)
    }
} 
Example 13
Source File: EnumSpec.scala    From finagle-postgres   with Apache License 2.0 5 votes vote down vote up
package com.twitter.finagle.postgres.generic

import java.nio.charset.StandardCharsets

import com.twitter.finagle.postgres.generic.enumeration.InvalidValue
import com.twitter.finagle.postgres.values.{ValueDecoder, ValueEncoder}
import com.twitter.util.{Return, Throw}
import io.netty.buffer.Unpooled
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class EnumSpec extends AnyFlatSpec with Matchers {

  sealed trait TestEnum
  case object CaseOne extends TestEnum
  case object CaseTwo extends TestEnum

  sealed trait AnotherBranch extends TestEnum
  case object CaseThree extends AnotherBranch

  val UTF8 = StandardCharsets.UTF_8


  "Enum decoding" should "decode enumeration ADTs from strings" in  {

    val decoder = ValueDecoder[TestEnum]

    decoder.decodeText("enum_recv", "CaseOne") shouldEqual Return(CaseOne)
    decoder.decodeText("enum_recv", "CaseTwo") shouldEqual Return(CaseTwo)
    decoder.decodeText("enum_recv", "CaseThree") shouldEqual Return(CaseThree)

    decoder.decodeBinary(
      "enum_recv",
      Unpooled.copiedBuffer("CaseOne", UTF8),
      UTF8
    ) shouldEqual Return(CaseOne)

    decoder.decodeBinary(
      "enum_recv",
      Unpooled.copiedBuffer("CaseTwo", UTF8),
      UTF8
    ) shouldEqual Return(CaseTwo)

    decoder.decodeBinary(
      "enum_recv",
      Unpooled.copiedBuffer("CaseThree", UTF8),
      UTF8
    ) shouldEqual Return(CaseThree)

  }

  it should "fail for an invalid value" in {
    val decoder = ValueDecoder[TestEnum]

    decoder.decodeText("enum_recv", "CasePurple") shouldEqual Throw(InvalidValue("CasePurple"))
    decoder.decodeBinary(
      "enum_recv",
      Unpooled.copiedBuffer("CasePurple", UTF8),
      UTF8
    ) shouldEqual Throw(InvalidValue("CasePurple"))

  }

  "Enum encoding" should "encode enumeration ADTs to Strings" in {
    val encoder = ValueEncoder[TestEnum]
    encoder.encodeText(CaseOne) shouldEqual Some("CaseOne")
    encoder.encodeText(CaseTwo) shouldEqual Some("CaseTwo")
    encoder.encodeText(CaseThree) shouldEqual Some("CaseThree")
    encoder.encodeBinary(CaseOne, UTF8).get.toString(UTF8) shouldEqual "CaseOne"
    encoder.encodeBinary(CaseTwo, UTF8).get.toString(UTF8) shouldEqual "CaseTwo"
    encoder.encodeBinary(CaseThree, UTF8).get.toString(UTF8) shouldEqual "CaseThree"
  }

} 
Example 14
Source File: try.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util

import cats.instances.either._
import cats.instances.int._
import cats.instances.option._
import cats.instances.tuple._
import cats.instances.unit._
import cats.kernel.laws.discipline.{ MonoidTests, SemigroupTests }
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.{ MonadErrorTests, TraverseTests }
import com.twitter.util.{ Return, Throw, Try }
import org.scalatest.propspec.AnyPropSpec
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks

trait TryTest extends ArbitraryInstances with EqInstances with TryInstances

class TrySuite extends CatbirdSuite with TryTest {
  checkAll("Try[Int]", MonadErrorTests[Try, Throwable].monadError[Int, Int, Int])
  checkAll("Try[Int]", TraverseTests[Try].traverse[Int, Int, Int, Int, Option, Option])
  checkAll("Try[Int]", SemigroupTests[Try[Int]](twitterTrySemigroup[Int]).semigroup)
  checkAll("Try[Int]", MonoidTests[Try[Int]].monoid)
}

class TrySpec extends AnyPropSpec with ScalaCheckPropertyChecks with TryTest {
  property("Equality for Try should use universal equality for Throw") {
    case class SomeError(message: String) extends Exception(message)

    forAll((s: String) => assert(twitterTryEq[Int].eqv(Throw(SomeError(s)), Throw(SomeError(s)))))
  }

  property("Equality for Try should never mix up Return and Throw") {
    forAll((i: Int) => assert(!twitterTryEq[Int].eqv(Throw(new Exception), Return(i))))
  }
} 
Example 15
Source File: try.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util

import cats.{ Applicative, CoflatMap, Eq, Eval, MonadError, Monoid, Semigroup, Traverse }
import com.twitter.util.{ Return, Throw, Try }
import java.lang.Throwable
import scala.{ Boolean, inline }
import scala.annotation.tailrec
import scala.util.{ Either, Left, Right }

trait TryInstances extends TryInstances1 {
  implicit final def twitterTryEq[A](implicit A: Eq[A], T: Eq[Throwable]): Eq[Try[A]] =
    new Eq[Try[A]] {
      def eqv(x: Try[A], y: Try[A]): Boolean = (x, y) match {
        case (Throw(xError), Throw(yError))   => T.eqv(xError, yError)
        case (Return(xValue), Return(yValue)) => A.eqv(xValue, yValue)
        case _                                => false
      }
    }

  implicit final def twitterTrySemigroup[A](implicit A: Semigroup[A]): Semigroup[Try[A]] =
    new TrySemigroup[A]

  implicit final val twitterTryInstance: MonadError[Try, Throwable] with CoflatMap[Try] with Traverse[Try] =
    new MonadError[Try, Throwable] with CoflatMap[Try] with Traverse[Try] {
      final def pure[A](x: A): Try[A] = Return(x)
      final def flatMap[A, B](fa: Try[A])(f: A => Try[B]): Try[B] = fa.flatMap(f)
      override final def map[A, B](fa: Try[A])(f: A => B): Try[B] = fa.map(f)

      final def handleErrorWith[A](fa: Try[A])(f: Throwable => Try[A]): Try[A] = fa.rescue {
        case e => f(e)
      }
      final def raiseError[A](e: Throwable): Try[A] = Throw(e)

      final def coflatMap[A, B](ta: Try[A])(f: Try[A] => B): Try[B] = Try(f(ta))

      final def foldLeft[A, B](fa: Try[A], b: B)(f: (B, A) => B): B = fa match {
        case Return(a) => f(b, a)
        case Throw(_)  => b
      }

      final def foldRight[A, B](fa: Try[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = fa match {
        case Return(a) => f(a, lb)
        case Throw(_)  => lb
      }

      final def traverse[G[_], A, B](fa: Try[A])(f: A => G[B])(implicit G: Applicative[G]): G[Try[B]] = fa match {
        case Return(a)   => G.map(f(a))(Return(_))
        case t: Throw[_] => G.pure(TryInstances.castThrow[B](t))
      }

      @tailrec final def tailRecM[A, B](a: A)(f: A => Try[Either[A, B]]): Try[B] = f(a) match {
        case t: Throw[_]      => TryInstances.castThrow[B](t)
        case Return(Left(a1)) => tailRecM(a1)(f)
        case Return(Right(b)) => Return(b)
      }
    }
}

private[util] final object TryInstances {
  @inline final def castThrow[A](t: Throw[_]): Try[A] = t.asInstanceOf[Try[A]]
}

private[util] trait TryInstances1 {
  implicit final def twitterTryMonoid[A](implicit A: Monoid[A]): Monoid[Try[A]] =
    new TrySemigroup[A] with Monoid[Try[A]] {
      final def empty: Try[A] = Return(A.empty)
    }
}

private[util] class TrySemigroup[A](implicit A: Semigroup[A]) extends Semigroup[Try[A]] {
  final def combine(fx: Try[A], fy: Try[A]): Try[A] = fx.flatMap(x => fy.map(y => A.combine(x, y)))
} 
Example 16
Source File: RerunnableInstances.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util.effect

import cats.effect.{ Effect, ExitCase, IO, SyncIO }
import com.twitter.util.{ Future, Monitor, Promise, Return, Throw }
import io.catbird.util.{ Rerunnable, RerunnableMonadError }
import java.lang.Throwable
import scala.Unit
import scala.util.{ Either, Left, Right }

trait RerunnableInstances {
  implicit final val rerunnableEffectInstance: Effect[Rerunnable] =
    new RerunnableMonadError with Effect[Rerunnable] {
      final def suspend[A](thunk: => Rerunnable[A]): Rerunnable[A] = Rerunnable.suspend[A](thunk)

      override final def delay[A](thunk: => A): Rerunnable[A] = Rerunnable[A](thunk)

      final def async[A](k: (Either[Throwable, A] => Unit) => Unit): Rerunnable[A] =
        new Rerunnable[A] {
          final def run: Future[A] = {
            val promise = new Promise[A]

            k { e =>
              if (promise.isDefined) ()
              else
                e match {
                  case Right(a)  => promise.setValue(a)
                  case Left(err) => promise.setException(err)
                }
            }

            promise
          }
        }

      final def asyncF[A](k: (Either[Throwable, A] => Unit) => Rerunnable[Unit]): Rerunnable[A] =
        new Rerunnable[A] {
          final def run: Future[A] = {
            val promise = new Promise[A]

            val rerunnable = k { e =>
              if (promise.isDefined) ()
              else
                e match {
                  case Right(a)  => promise.setValue(a)
                  case Left(err) => promise.setException(err)
                }
            }

            rerunnable.run.flatMap(_ => promise)
          }
        }

      final def runAsync[A](fa: Rerunnable[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit] =
        rerunnableToIO[A](fa).runAsync(cb)

      final def bracketCase[A, B](acquire: Rerunnable[A])(use: A => Rerunnable[B])(
        release: (A, ExitCase[Throwable]) => Rerunnable[Unit]
      ): Rerunnable[B] = new Rerunnable[B] {
        final def run: Future[B] =
          acquire.run.flatMap { a =>
            val future = use(a).run
            future.transform {
              case Return(b)  => release(a, ExitCase.complete).run.handle(Monitor.catcher).flatMap(_ => future)
              case Throw(err) => release(a, ExitCase.error(err)).run.handle(Monitor.catcher).flatMap(_ => future)
            }
          }
      }
    }
} 
Example 17
Source File: ScodecSerial.scala    From finagle-serial   with Apache License 2.0 5 votes vote down vote up
package io.github.finagle.serial.scodec

import _root_.scodec.{Codec, Err}
import _root_.scodec.bits.BitVector
import _root_.scodec.codecs._
import com.twitter.io.Buf
import com.twitter.util.{Return, Throw, Try}
import io.github.finagle.serial.{ApplicationError, CodecError, Serial}

trait ScodecSerial extends Serial {
  type C[A] = Codec[A]
  type Bytes = BitVector

  
  lazy val applicationErrorCodec: Codec[Throwable] = ApplicationErrorCodec.basic.underlying

  private[this] def reqMessageCodec[A](c: Codec[A]): Codec[Either[CodecError, A]] =
    either(bool, codecErrorCodec, c)

  def encodeReq[A](a: A)(c: Codec[A]): Try[BitVector] = {
    val codec = reqMessageCodec(c)

    codec.encode(Right(a)).fold(
      e => codec.encode(Left(CodecError(e.message))).fold(
        e => Throw(CodecError(e.message)),
        bits => Return(bits)
      ),
      bits => Return(bits)
    )
  }

  def decodeReq[A](bytes: BitVector)(c: Codec[A]): Try[A] =
    reqMessageCodec(c).decode(bytes).fold(
      e => Throw(CodecError(e.message)),
      o => o.value.fold(Throw(_), Return(_))
    )

  def encodeRep[A](t: Try[A])(c: Codec[A]): Try[BitVector] = {
    val message: RepMessage[A] = t match {
      case Return(a) => ContentRepMessage(a)
      case Throw(e @ CodecError(_)) => CodecErrorRepMessage(e)
      case Throw(e) => ApplicationErrorRepMessage(e)
    }

    val codec = RepMessage.codec(
      c,
      codecErrorCodec,
      applicationErrorCodec,
      unhandledApplicationErrorCodec
    )

    codec.encode(message).fold(
      {
        case Err.MatchingDiscriminatorNotFound(t: Throwable, _) =>
          codec.encode(UnhandledApplicationErrorRepMessage(ApplicationError(t.toString))).fold(
            e => Throw(CodecError(e.message)),
            bits => Return(bits)
          )
        case e => codec.encode(CodecErrorRepMessage(CodecError(e.message))).fold(
          e => Throw(CodecError(e.message)),
          bits => Return(bits)
        )
      },
      bits => Return(bits)
    )
  }

  def decodeRep[A](bytes: BitVector)(c: Codec[A]): Try[A] =
    RepMessage.codec(
      c,
      codecErrorCodec,
      applicationErrorCodec,
      unhandledApplicationErrorCodec
    ).decode(bytes).fold(
      e => Throw(CodecError(e.message)),
      o => o.value.toTry
    )

  def toBuf(bytes: BitVector): Buf = Buf.ByteArray.Owned(bytes.toByteArray)
  def fromBuf(buf: Buf): BitVector = BitVector(Buf.ByteArray.Owned.extract(buf))
}

object ScodecSerial extends ScodecSerial 
Example 18
Source File: RepMessage.scala    From finagle-serial   with Apache License 2.0 5 votes vote down vote up
package io.github.finagle.serial.scodec

import _root_.scodec.Codec
import _root_.scodec.codecs._
import com.twitter.util.{Return, Throw, Try}
import io.github.finagle.serial.{CodecError, ApplicationError}

private[scodec] sealed trait RepMessage[A] {
  def toTry: Try[A]
}

private[scodec] case class ContentRepMessage[A](a: A) extends RepMessage[A] {
  def toTry: Try[A] = Return(a)
}

private[scodec] case class CodecErrorRepMessage[A](err: CodecError) extends RepMessage[A] {
  def toTry: Try[A] = Throw(err)
}

private[scodec] case class ApplicationErrorRepMessage[A](err: Throwable) extends RepMessage[A] {
  def toTry: Try[A] = Throw(err)
}

private[scodec] case class UnhandledApplicationErrorRepMessage[A](err: ApplicationError)
  extends RepMessage[A] {
    def toTry: Try[A] = Throw(err)
  }

private[scodec] object RepMessage {
  def codec[A](
    aCodec: Codec[A],
    codecErrorCodec: Codec[CodecError],
    applicationErrorCodec: Codec[Throwable],
    unhandledApplicationErrorCodec: Codec[ApplicationError]
  ): Codec[RepMessage[A]] = (
    aCodec.as[ContentRepMessage[A]] :+:
    codecErrorCodec.as[CodecErrorRepMessage[A]] :+:
    applicationErrorCodec.as[ApplicationErrorRepMessage[A]] :+:
    unhandledApplicationErrorCodec.as[UnhandledApplicationErrorRepMessage[A]]
  ).discriminatedByIndex(uint4).as[RepMessage[A]]
} 
Example 19
Source File: twitter.scala    From interop-twitter   with Apache License 2.0 5 votes vote down vote up
package zio.interop

import com.twitter.util.{ Future, FutureCancelledException, Promise, Return, Throw }
import zio.Cause
import zio.{ Runtime, Task, UIO, ZIO }

package object twitter {
  implicit class TaskObjOps(private val obj: Task.type) extends AnyVal {
    final def fromTwitterFuture[A](future: Task[Future[A]]): Task[A] =
      Task.uninterruptibleMask { restore =>
        future.flatMap { f =>
          restore(Task.effectAsync { cb: (Task[A] => Unit) =>
            val _ = f.respond {
              case Return(a) => cb(Task.succeed(a))
              case Throw(e)  => cb(Task.fail(e))
            }
          }).onInterrupt(UIO(f.raise(new FutureCancelledException)))
        }
      }
  }

  implicit class RuntimeOps[R](private val runtime: Runtime[R]) extends AnyVal {
    def unsafeRunToTwitterFuture[A](io: ZIO[R, Throwable, A]): Future[A] = {
      val promise                                = Promise[A]()
      def failure(cause: Cause[Throwable]): Unit = promise.setException(cause.squash)
      def success(value: A): Unit                = promise.setValue(value)

      runtime.unsafeRunAsync {
        io.fork.flatMap { f =>
          promise.setInterruptHandler {
            case _ => runtime.unsafeRunAsync_(f.interrupt)
          }
          f.join
        }
      }(_.fold(failure, success))

      promise
    }
  }
} 
Example 20
Source File: UserContext.scala    From the-finagle-docs   with MIT License 5 votes vote down vote up
package net.gutefrage.context

import com.twitter.finagle.context.Contexts
import com.twitter.finagle.util.ByteArrays
import com.twitter.io.Buf
import com.twitter.util.{Return, Throw, Try}

case class UserContext(userId: Long)


  def current: Option[UserContext] = Contexts.broadcast.get(UserContext)

  override def marshal(userContext: UserContext): Buf = {
    val bytes = new Array[Byte](bodyLengthBytes)
    ByteArrays.put64be(bytes, 0, userContext.userId)
    Buf.ByteArray.Owned(bytes)
  }

  override def tryUnmarshal(body: Buf): Try[UserContext] = {
    if (body.length != bodyLengthBytes) {
      return Throw(new IllegalArgumentException(s"Invalid body. Length ${body.length} but required 16"))
    }

    val bytes = Buf.ByteArray.Owned.extract(body)
    val userId = ByteArrays.get64be(bytes, 0)

    Return(UserContext(userId))
  }
} 
Example 21
Source File: FinagleBackend.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.finagle

import java.util.concurrent.atomic.AtomicReference

import com.twitter.finagle.Service
import com.twitter.finagle.context.Contexts
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.{Future, Promise, Return, Throw}
import wvlet.airframe.http.{HttpBackend, HttpRequestAdapter, HttpStatus}
import wvlet.log.LogSupport

import scala.concurrent.ExecutionContext
import scala.util.{Failure, Success}
import scala.{concurrent => sc}


object FinagleBackend extends HttpBackend[Request, Response, Future] {
  override protected implicit val httpRequestAdapter: HttpRequestAdapter[Request] = FinagleHttpRequestAdapter

  override def wrapException(e: Throwable): Future[Response] = {
    Future.exception(e)
  }
  override def newResponse(status: HttpStatus, content: String): Response = {
    val r = Response(Status.fromCode(status.code))
    r.contentString = content
    r
  }

  def wrapFilter(filter: com.twitter.finagle.Filter[Request, Response, Request, Response]): FinagleFilter = {
    new FinagleFilter with LogSupport {
      override def apply(request: Request, context: Context): Future[Response] = {
        filter(request, Service.mk { req: Request => context(req) })
      }
    }
  }

  override def toFuture[A](a: A): Future[A] = Future.value(a)
  override def toScalaFuture[A](a: Future[A]): sc.Future[A] = {
    val promise: sc.Promise[A] = sc.Promise()
    a.respond {
      case Return(value)    => promise.success(value)
      case Throw(exception) => promise.failure(exception)
    }
    promise.future
  }
  override def toFuture[A](a: sc.Future[A], e: ExecutionContext): Future[A] = {
    val promise: Promise[A] = Promise()
    a.onComplete {
      case Success(value)     => promise.setValue(value)
      case Failure(exception) => promise.setException(exception)
    }(e)
    promise
  }

  override def isFutureType(cl: Class[_]): Boolean = {
    classOf[Future[_]].isAssignableFrom(cl)
  }
  override def isRawResponseType(cl: Class[_]): Boolean = {
    classOf[Response].isAssignableFrom(cl)
  }
  override def mapF[A, B](f: Future[A], body: A => B): Future[B] = {
    f.map(body)
  }

  private val contextParamHolderKey = new Contexts.local.Key[AtomicReference[collection.mutable.Map[String, Any]]]

  override def withThreadLocalStore(body: => Future[Response]): Future[Response] = {
    val newParamHolder = collection.mutable.Map.empty[String, Any]
    Contexts.local
      .let(contextParamHolderKey, new AtomicReference[collection.mutable.Map[String, Any]](newParamHolder)) {
        body
      }
  }

  override def setThreadLocal[A](key: String, value: A): Unit = {
    Contexts.local.get(contextParamHolderKey).foreach { ref => ref.get().put(key, value) }
  }

  override def getThreadLocal[A](key: String): Option[A] = {
    Contexts.local.get(contextParamHolderKey).flatMap { ref => ref.get.get(key).asInstanceOf[Option[A]] }
  }
} 
Example 22
Source File: ConstFuture.scala    From arrows   with Apache License 2.0 5 votes vote down vote up
package arrows.twitter

import com.twitter.util.Future
import com.twitter.util.Try
import com.twitter.util.Awaitable
import com.twitter.util.Duration
import com.twitter.util.Return
import scala.runtime.NonLocalReturnControl
import com.twitter.concurrent.Scheduler
import com.twitter.util.FutureNonLocalReturnControl
import com.twitter.util.Local
import com.twitter.util.Monitor
import scala.util.control.NonFatal
import com.twitter.util.Promise
import com.twitter.util.Throw

trait ConstFuture[T] extends Future[T] {
  final def isReady(implicit permit: Awaitable.CanAwait): Boolean = true

  override final def ready(timeout: Duration)(implicit permit: Awaitable.CanAwait) = this
  final def poll: Option[com.twitter.util.Try[T]] = Some(toTry)

  protected def toTry: Try[T]

  final def respond(k: Try[T] => Unit): Future[T] = {
    val saved = Local.save()
    Scheduler.submit(new Runnable {
      def run(): Unit = {
        val current = Local.save()
        Local.restore(saved)
        try k(toTry)
        catch Monitor.catcher
        finally Local.restore(current)
      }
    })
    this
  }

  final def raise(interrupt: Throwable): Unit = ()

  final def transform[B](f: Try[T] => Future[B]): Future[B] = {
    val p = new Promise[B]
    // see the note on `respond` for an explanation of why `Scheduler` is used.
    val saved = Local.save()
    Scheduler.submit(new Runnable {
      def run(): Unit = {
        val current = Local.save()
        Local.restore(saved)
        val computed = try f(toTry)
        catch {
          case e: NonLocalReturnControl[_] => Future.exception(new FutureNonLocalReturnControl(e))
          case NonFatal(e)                 => Future.exception(e)
          case t: Throwable =>
            Monitor.handle(t)
            throw t
        } finally Local.restore(current)
        p.become(computed)
      }
    })
    p
  }
}

class ReturnFuture[T](r: T) extends ConstFuture[T] {
  override final def result(timeout: Duration)(implicit permit: Awaitable.CanAwait): T = r
  override final def toTry = Return(r)
}

class ThrowFuture[T](ex: Throwable) extends ConstFuture[T] {
  override final def result(timeout: Duration)(implicit permit: Awaitable.CanAwait): T = throw ex
  override final def toTry = Throw(ex)
} 
Example 23
Source File: Arrays.scala    From finagle-postgres   with Apache License 2.0 5 votes vote down vote up
package com.twitter.finagle.postgres.values

import scala.collection.immutable.Queue
import scala.util.parsing.combinator.RegexParsers

import com.twitter.util.{Return, Throw, Try}
import io.netty.buffer.ByteBuf
object Arrays {

  object ArrayStringParser extends RegexParsers {

    val value = """([^",}]|\")*""".r | """"([^"]|\")*"""".r
    val valueComma = "," ~ value ^^ { case "," ~ v => v }
    val values = (value ~ valueComma.*) ^^ { case first ~ rest => first :: rest } | value.? ^^ (_.toList)
    val array = "{" ~ values ~ "}" ^^ { case _ ~ vs ~ _ => vs }
    val maybeArrayValue = array | value ^^ (List(_))
    val maybeArrayValueComma = ("," ~ maybeArrayValue) ^^ { case _ ~ v => v}
    val maybeArrayValues =
      (maybeArrayValue ~ maybeArrayValueComma.*) ^^ { case first ~ rest => first ::: rest.flatten } |
        maybeArrayValue.* ^^ (_.flatten)
    val root = "{" ~ maybeArrayValues ~ "}" ^^ {
      case _ ~ vs ~ _ => vs
    }

    def apply(str: String) = parseAll(root, str) match {
      case Success(strings, _) => Return(strings)
      case Failure(_, _) | Error(_, _) => Throw(new Exception("Failed to parse array string"))
    }

  }

  // TODO: this isn't used anywhere, but it would need access to the type map and it would need to receive the elemoid
  def decodeArrayText[T](str: String, elemDecoder: ValueDecoder[T]) = {
    ArrayStringParser(str).flatMap {
      strings => strings.map(str => elemDecoder.decodeText("", str)).foldLeft[Try[Queue[T]]](Return(Queue.empty[T])) {
        (accum, next) => accum.flatMap {
          current => next.map(v => current enqueue v)
        }
      }
    }
  }

  def decodeArrayBinary[T](buf: ByteBuf, elemDecoder: ValueDecoder[T]) = {
    val ndims = buf.readInt()
    val flags = buf.readInt()
    val elemOid = buf.readInt()
  }

}