-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from penland365/results
Decoding Int and String from Result.
- Loading branch information
Showing
10 changed files
with
277 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.github.finagle | ||
package roc | ||
package postgresql | ||
|
||
import cats.data.Xor | ||
|
||
trait ByteDecoder[A] { | ||
def fromText(bytes: Option[Array[Byte]]): ByteDecodingFailure Xor Option[A] | ||
def fromBinary(bytes: Option[Array[Byte]]): Error Xor Option[A] | ||
} | ||
|
||
sealed trait FormatCode | ||
case object Text extends FormatCode | ||
case object Binary extends FormatCode | ||
|
||
object `package` extends ByteDecoderImplicits | ||
|
||
trait ByteDecoderImplicits { | ||
|
||
implicit val intByteDecoder: ByteDecoder[Int] = new ByteDecoder[Int] { | ||
def fromText(bytes: Option[Array[Byte]]): ByteDecodingFailure Xor Option[Int] = | ||
bytes match { | ||
case Some(b) => { | ||
val int = b.map(_.toChar).mkString.toInt | ||
Xor.Right(Some(int)) | ||
} | ||
case None => Xor.Right(None) | ||
} | ||
|
||
def fromBinary(bytes: Option[Array[Byte]]): Error Xor Option[Int] = | ||
Xor.Left(new UnsupportedDecodingFailure("Decoding int from Binary is not Supported")) | ||
} | ||
|
||
implicit val stringByteDecoder: ByteDecoder[String] = new ByteDecoder[String] { | ||
def fromText(bytes: Option[Array[Byte]]): ByteDecodingFailure Xor Option[String] = | ||
bytes match { | ||
case Some(b) => { | ||
val str = b.map(_.toChar).mkString | ||
Xor.Right(Some(str)) | ||
} | ||
case None => Xor.Right(None) | ||
} | ||
|
||
def fromBinary(bytes: Option[Array[Byte]]): Error Xor Option[String] = | ||
Xor.Left(new UnsupportedDecodingFailure("Decoding string from Binary is not Supported")) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.github.finagle | ||
package roc | ||
package postgresql | ||
|
||
sealed trait Error extends Exception | ||
|
||
final class UnknownPostgresTypeFailure(objectId: Int) extends Error { | ||
final override def getMessage: String = s"Postgres Object ID $objectId is unknown" | ||
|
||
def canEqual(a: Any) = a.isInstanceOf[UnknownPostgresTypeFailure] | ||
|
||
final override def equals(that: Any): Boolean = that match { | ||
case x: UnknownPostgresTypeFailure => x.canEqual(this) && getMessage == x.getMessage | ||
case _ => false | ||
} | ||
} | ||
|
||
final class ByteDecodingFailure(message: String) extends Error { | ||
final override def getMessage: String = message | ||
} | ||
|
||
final class UnsupportedDecodingFailure(message: String) extends Error { | ||
final override def getMessage: String = message | ||
} | ||
|
||
final class UnexpectedNoneFailure(message: String) extends Error { | ||
final override def getMessage: String = message | ||
} | ||
|
||
final class InvalidAuthenticationRequest(authType: Int) extends Error { | ||
final override def getMessage: String = | ||
s"Got invalid and unkown Authentication Request: $authType" | ||
} | ||
|
||
final class ColumnNotFoundException(symbol: Symbol) extends Error { | ||
final override def getMessage: String = s"Could not find column $symbol in Result" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.github.finagle | ||
package roc | ||
package postgresql | ||
|
||
import cats.data.Xor | ||
import cats.Show | ||
import com.github.finagle.roc.postgresql.transport.BufferReader | ||
import com.twitter.util.Future | ||
|
||
final class Result(rowDescription: RowDescription, data: List[DataRow]) { | ||
|
||
val columns = rowDescription.fields | ||
.map(x => Symbol(x.name)) | ||
.zip(rowDescription.fields) | ||
.map(tuple => { | ||
val postgresType = PostgresType(tuple._2.dataTypeObjectId) match { | ||
case Xor.Right(t) => t | ||
case Xor.Left(l) => throw l | ||
} | ||
new Column(tuple._1, postgresType, tuple._2.formatCode) | ||
}) | ||
|
||
val rows = data.map(x => new Row(columns, x)) | ||
} | ||
|
||
final case class Column(name: Symbol, columnType: PostgresType, formatCode: FormatCode) { | ||
final override def toString: String = Column.columnShow.show(this) | ||
} | ||
object Column { | ||
|
||
implicit val columnShow: Show[Column] = new Show[Column] { | ||
def show(c: Column): String = | ||
s"Column(name=${c.name}, columnType=${c.columnType}, formatCode=${c.formatCode})" | ||
} | ||
} | ||
|
||
|
||
final class Row(private[postgresql] val columns: List[Column], dataRow: DataRow) { | ||
object `package` extends postgresql.ByteDecoderImplicits | ||
|
||
def get[A](columnName: Symbol)(implicit f: ByteDecoder[A]): A = { | ||
val idx = indexOfColumn(columnName) | ||
val column = columns(idx) | ||
column.formatCode match { | ||
case Text => f.fromText(dataRow.columnBytes(idx)) match { | ||
case Xor.Right(r) => r match { | ||
case Some(x) => x | ||
case None => throw new UnexpectedNoneFailure("Got Option when None was expected") | ||
} | ||
case Xor.Left(l) => throw l | ||
} | ||
case Binary => f.fromBinary(dataRow.columnBytes(idx)) match { | ||
case Xor.Right(r) => r match { | ||
case Some(x) => x | ||
case None => throw new UnexpectedNoneFailure("Got Option when None was expected") | ||
} | ||
case Xor.Left(l) => throw l | ||
} | ||
} | ||
} | ||
|
||
private[postgresql] def indexOfColumn(sym: Symbol): Int = | ||
columns.indexWhere(_.name == sym) match { | ||
case -1 => throw new ColumnNotFoundException(sym) | ||
case n => n | ||
} | ||
|
||
final override def toString: String = Row.rowShow.show(this) | ||
} | ||
object Row { | ||
implicit val rowShow: Show[Row] = new Show[Row] { | ||
def show(r: Row): String = | ||
s"Row(columns=${r.columns})" | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.github.finagle | ||
package roc | ||
package postgresql | ||
|
||
import cats.data.Xor | ||
|
||
trait PostgresType | ||
object PostgresType { | ||
|
||
def apply(objectId: Int): UnknownPostgresTypeFailure Xor PostgresType = objectId match { | ||
case Int4OID => Xor.right(Int4) | ||
case VarCharOID => Xor.right(VarChar) | ||
case TimestampWithTimezoneOID => Xor.right(TimestampWithTimezone) | ||
case n => Xor.left(new UnknownPostgresTypeFailure(n)) | ||
} | ||
|
||
private[this] val Int4OID = 23 | ||
private[this] val VarCharOID = 1043 | ||
private[this] val TimestampWithTimezoneOID = 1184 | ||
|
||
// used for testing only | ||
private[postgresql] val oids = List(Int4OID, VarCharOID, TimestampWithTimezoneOID) | ||
} | ||
|
||
case object Int4 extends PostgresType | ||
case object VarChar extends PostgresType | ||
case object TimestampWithTimezone extends PostgresType |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.github.finagle | ||
package roc | ||
package postgresql | ||
|
||
import org.scalacheck.Prop.forAll | ||
import org.specs2._ | ||
import org.specs2.specification.core._ | ||
|
||
final class ErrorsSpec extends Specification with ScalaCheck { def is = s2""" | ||
|
||
Error | ||
UnknownPostgresTypeFailure should have correct message $unkownPostgresTypeFailure | ||
""" | ||
val unkownPostgresTypeFailure = forAll { n: Int => | ||
val msg = s"Postgres Object ID $n is unknown" | ||
val error = new UnknownPostgresTypeFailure(n) | ||
error.getMessage must_== msg | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.github.finagle | ||
package roc | ||
package postgresql | ||
|
||
import org.scalacheck.Arbitrary.arbitrary | ||
import org.scalacheck.Prop.forAll | ||
import org.scalacheck.{Arbitrary, Gen} | ||
import org.specs2._ | ||
import org.specs2.mock.Mockito | ||
import org.specs2.specification.core._ | ||
|
||
final class ResultsSpec extends Specification with ScalaCheck with Mockito { def is = s2""" | ||
|
||
Row | ||
get[A](column) should throw ColumnNotFoundException for unknown column $columnNotFound | ||
""" | ||
|
||
val columnNotFound = forAll { sym: Symbol => | ||
val m = mock[DataRow] | ||
val row = new Row(List.empty[Column], m) | ||
row.get[String](sym) must throwA[ColumnNotFoundException] | ||
} | ||
|
||
lazy val genSymbol: Gen[Symbol] = for { | ||
str <- arbitrary[String] | ||
} yield Symbol(str) | ||
implicit lazy val arbitrarySymbol: Arbitrary[Symbol] = | ||
Arbitrary(genSymbol) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.github.finagle | ||
package roc | ||
package postgresql | ||
|
||
import cats.data.Xor | ||
import org.scalacheck.Gen | ||
import org.scalacheck.Prop.forAll | ||
import org.specs2._ | ||
import org.specs2.specification.core._ | ||
|
||
final class TypesSpec extends Specification with ScalaCheck { def is = s2""" | ||
|
||
PostgresType | ||
should create Int4 $int4 | ||
should create VarChar $varChar | ||
should create TimestampWithTimezone $timestampWithTimezone | ||
should create UnknownPostgresTypeFailure for invalid type $unknownOID | ||
""" | ||
def int4 = PostgresType(23) must_== Xor.Right(Int4) | ||
def varChar = PostgresType(1043) must_== Xor.Right(VarChar) | ||
def timestampWithTimezone = PostgresType(1184) must_== Xor.Right(TimestampWithTimezone) | ||
|
||
val unknownOID = forAll(unknownOIDInt) { n => | ||
val error = new UnknownPostgresTypeFailure(n) | ||
PostgresType(n) must_== Xor.Left(error) | ||
} | ||
|
||
lazy val unknownOIDInt: Gen[Int] = Gen.choose(Int.MinValue, Int.MaxValue) | ||
.suchThat(!PostgresType.oids.contains(_)) | ||
} | ||
|