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

Streamline instances #2210

Merged
merged 10 commits into from Nov 2, 2018
Merged
Expand Up @@ -5,6 +5,7 @@ package blaze
import cats.effect._
import cats.implicits._
import fs2.Stream._
import org.http4s.implicits._
import org.http4s.Charset._
import org.http4s.dsl.Http4sDsl
import org.http4s.headers._
Expand Down
Expand Up @@ -10,6 +10,7 @@ import org.http4s.MediaType
import org.http4s.Status.{BadRequest, Created, InternalServerError, Ok}
import org.http4s.client.dsl.Http4sClientDsl
import org.http4s.headers.Accept
import org.http4s.Uri.uri
import org.specs2.matcher.MustThrownMatchers

class ClientSyntaxSpec extends Http4sSpec with Http4sClientDsl[IO] with MustThrownMatchers {
Expand Down
Expand Up @@ -9,6 +9,7 @@ import java.util.concurrent.atomic._
import org.http4s.client.dsl.Http4sClientDsl
import org.http4s.dsl.io._
import org.http4s.headers._
import org.http4s.Uri.uri
import org.specs2.mutable.Tables

class FollowRedirectSpec extends Http4sSpec with Http4sClientDsl[IO] with Tables {
Expand Down
Expand Up @@ -4,6 +4,7 @@ package middleware

import cats.effect._
import fs2.io.readInputStream
import org.http4s.Uri.uri
import org.http4s.dsl.io._
import scala.io.Source

Expand Down
Expand Up @@ -7,6 +7,7 @@ import cats.effect.concurrent.Ref
import cats.implicits.{catsSyntaxEither => _, _}
import fs2.Stream
import org.http4s.dsl.io._
import org.http4s.Uri.uri
import org.specs2.specification.Tables
import scala.concurrent.duration._

Expand Down
13 changes: 4 additions & 9 deletions core/src/main/scala/org/http4s/CharsetRange.scala
@@ -1,6 +1,6 @@
package org.http4s

import cats._
import cats.{Eq, Show}
import org.http4s.util._

sealed abstract class CharsetRange extends HasQValue with Renderable {
Expand All @@ -20,7 +20,7 @@ sealed abstract class CharsetRange extends HasQValue with Renderable {
}
}

object CharsetRange extends CharsetRangeInstances {
object CharsetRange {
sealed case class `*`(qValue: QValue) extends CharsetRange {
final override def withQValue(q: QValue): CharsetRange.`*` = copy(qValue = q)

Expand All @@ -43,12 +43,7 @@ object CharsetRange extends CharsetRangeInstances {
}

implicit def fromCharset(cs: Charset): CharsetRange.Atom = cs.toRange
}

trait CharsetRangeInstances {
implicit val http4sEqForCharsetRange: Eq[CharsetRange] =
Eq.fromUniversalEquals

implicit val http4sShowForCharsetRange: Show[Charset] =
Show.fromToString
implicit val http4sEqForCharsetRange: Eq[CharsetRange] = Eq.fromUniversalEquals
implicit val http4sShowForCharsetRange: Show[CharsetRange] = Show.fromToString
}
23 changes: 10 additions & 13 deletions core/src/main/scala/org/http4s/ContentCoding.scala
Expand Up @@ -18,10 +18,10 @@
*/
package org.http4s

import cats.{Order, Show}
import cats.syntax.eq._
import cats.instances.tuple._
import cats.instances.string._
import cats.instances.tuple._
import cats.syntax.eq._
import cats.{Order, Show}
import org.http4s.QValue.QValueParser
import org.http4s.internal.parboiled2.{Parser => PbParser, _}
import org.http4s.parser.Http4sParser
Expand Down Expand Up @@ -65,7 +65,7 @@ class ContentCoding private (val coding: String, override val qValue: QValue = Q
ContentCoding.http4sOrderForContentCoding.compare(this, other)

override def render(writer: Writer): writer.type =
ContentCoding.http4sInstancesForContentCoding.render(writer, this)
ContentCoding.http4sHttpCodecForContentCoding.render(writer, this)
}

object ContentCoding {
Expand Down Expand Up @@ -116,19 +116,16 @@ object ContentCoding {
}
}

implicit val http4sInstancesForContentCoding: Show[ContentCoding] with HttpCodec[ContentCoding] =
new Show[ContentCoding] with HttpCodec[ContentCoding] {
override def show(s: ContentCoding): String = s.toString

implicit val http4sOrderForContentCoding: Order[ContentCoding] =
Order.by(c => (c.coding.toLowerCase, c.qValue))
implicit val http4sShowForContentCoding: Show[ContentCoding] =
Show.fromToString
implicit val http4sHttpCodecForContentCoding: HttpCodec[ContentCoding] =
new HttpCodec[ContentCoding] {
override def parse(s: String): ParseResult[ContentCoding] =
ContentCoding.parse(s)

override def render(writer: Writer, coding: ContentCoding): writer.type =
writer << coding.coding.toLowerCase << coding.qValue

}

implicit val http4sOrderForContentCoding: Order[ContentCoding] =
Order.by(c => (c.coding.toLowerCase, c.qValue))

}
9 changes: 3 additions & 6 deletions core/src/main/scala/org/http4s/EntityDecoder.scala
Expand Up @@ -16,6 +16,7 @@ import scala.concurrent.ExecutionContext
* entire resulting A. If an error occurs it will result in a failed effect.
* The default decoders provided here are not streaming, but one could implement
* a streaming decoder by having the value of A be some kind of streaming construct.
*
* @tparam T result type produced by the decoder
*/
@implicitNotFound(
Expand Down Expand Up @@ -93,6 +94,7 @@ trait EntityDecoder[F[_], T] { self =>
*
* The new [[EntityDecoder]] will first attempt to determine if it can perform the decode,
* and if not, defer to the second [[EntityDecoder]]
*
* @param other backup [[EntityDecoder]]
*/
def orElse[T2 >: T](other: EntityDecoder[F, T2])(implicit F: Functor[F]): EntityDecoder[F, T2] =
Expand All @@ -110,7 +112,7 @@ trait EntityDecoder[F[_], T] { self =>
* This companion object provides a way to create `new EntityDecoder`s along
* with some commonly used instances which can be resolved implicitly.
*/
object EntityDecoder extends EntityDecoderInstances {
object EntityDecoder {

// This is not a real media type but will still be matched by `*/*`
private val UndefinedMediaType = new MediaType("UNKNOWN", "UNKNOWN")
Expand Down Expand Up @@ -185,11 +187,6 @@ object EntityDecoder extends EntityDecoderInstances {
def decodeString[F[_]: Sync](msg: Message[F])(
implicit defaultCharset: Charset = DefaultCharset): F[String] =
msg.bodyAsText.compile.foldMonoid
}

/** Implementations of the EntityDecoder instances */
trait EntityDecoderInstances {
import org.http4s.EntityDecoder._

/////////////////// Instances //////////////////////////////////////////////

Expand Down
16 changes: 4 additions & 12 deletions core/src/main/scala/org/http4s/EntityEncoder.scala
Expand Up @@ -3,8 +3,8 @@ package org.http4s
import cats._
import cats.effect.{ContextShift, Effect, Sync}
import cats.implicits._
import fs2._
import fs2.Stream._
import fs2._
import fs2.io.file.readAll
import fs2.io.readInputStream
import java.io._
Expand Down Expand Up @@ -48,7 +48,9 @@ trait EntityEncoder[F[_], A] { self =>
}
}

object EntityEncoder extends EntityEncoderInstances {
object EntityEncoder {

private val DefaultChunkSize = 4096

/** summon an implicit [[EntityEncoder]] */
def apply[F[_], A](implicit ev: EntityEncoder[F, A]): EntityEncoder[F, A] = ev
Expand All @@ -75,10 +77,6 @@ object EntityEncoder extends EntityEncoderInstances {
val c = toChunk(a)
Entity[F](chunk(c).covary[F], Some(c.size.toLong))
}
}

trait EntityEncoderInstances0 {
import EntityEncoder._

/** Encodes a value from its Show instance. Too broad to be implicit, too useful to not exist. */
def showEncoder[F[_], A](
Expand Down Expand Up @@ -113,12 +111,6 @@ trait EntityEncoderInstances0 {
W.headers.put(`Transfer-Encoding`(TransferCoding.chunked))
}
}
}

trait EntityEncoderInstances extends EntityEncoderInstances0 {
import EntityEncoder._

private val DefaultChunkSize = 4096

implicit def unitEncoder[F[_]]: EntityEncoder[F, Unit] =
emptyEncoder[F, Unit]
Expand Down
15 changes: 7 additions & 8 deletions core/src/main/scala/org/http4s/Http4s.scala
@@ -1,20 +1,19 @@
package org.http4s

@deprecated("Use org.http4s.implicits._ instead", "0.20.0-M2")
trait Http4s extends Http4sInstances with Http4sFunctions with syntax.AllSyntax

@deprecated("Use org.http4s.implicits._ instead", "0.20.0-M2")
object Http4s extends Http4s

@deprecated("Import from or use EntityDecoder/EntityEncoder directly instead", "0.20.0-M2")
trait Http4sInstances
extends EntityDecoderInstances
with HttpVersionInstances
with EntityEncoderInstances
with CharsetRangeInstances
with QValueInstances
with MethodInstances
with StatusInstances

@deprecated("Import from or use EntityDecoder/EntityEncoder directly instead", "0.20.0-M2")
object Http4sInstances extends Http4sInstances

trait Http4sFunctions extends QValueFunctions with UriFunctions
@deprecated("Use org.http4s.qvalue._ or org.http4s.Uri._ instead", "0.20.0-M2")
trait Http4sFunctions

@deprecated("Use org.http4s.qvalue._ or org.http4s.Uri._ instead", "0.20.0-M2")
object Http4sFunctions extends Http4sFunctions
12 changes: 6 additions & 6 deletions core/src/main/scala/org/http4s/HttpVersion.scala
@@ -1,8 +1,8 @@
package org.http4s

import cats._
import cats.data.{Writer => _}
import cats.implicits._
import cats.{Order, Show}
import org.http4s.internal.parboiled2._
import org.http4s.parser._
import org.http4s.util._
Expand All @@ -20,7 +20,7 @@ final case class HttpVersion private[HttpVersion] (major: Int, minor: Int)
(this.major, this.minor).compare((that.major, that.minor))
}

object HttpVersion extends HttpVersionInstances {
object HttpVersion {
val `HTTP/1.0` = new HttpVersion(1, 0)
val `HTTP/1.1` = new HttpVersion(1, 1)
val `HTTP/2.0` = new HttpVersion(2, 0)
Expand Down Expand Up @@ -50,9 +50,9 @@ object HttpVersion extends HttpVersionInstances {
else if (minor < 0) ParseResult.fail("Invalid HTTP version", s"major must be > 0: $minor")
else if (minor > 9) ParseResult.fail("Invalid HTTP version", s"major must be <= 9: $minor")
else ParseResult.success(new HttpVersion(major, minor))
}

trait HttpVersionInstances {
implicit val HttpVersionShow = Show.fromToString[HttpVersion]
implicit val HttpVersionOrder = Order.fromOrdering[HttpVersion]
implicit val http4sHttpOrderForVersion: Order[HttpVersion] =
Order.fromComparable
implicit val http4sHttpShowForVersion: Show[HttpVersion] =
Show.fromToString
}
47 changes: 21 additions & 26 deletions core/src/main/scala/org/http4s/MediaType.scala
Expand Up @@ -18,8 +18,8 @@
*/
package org.http4s

import cats.{Eq, Order, Show}
import cats.implicits._
import cats.{Eq, Order, Show}
import org.http4s.headers.MediaRangeAndQValue
import org.http4s.internal.parboiled2.{Parser => PbParser, _}
import org.http4s.parser.{Http4sParser, Rfc2616BasicRules}
Expand Down Expand Up @@ -142,31 +142,29 @@ object MediaRange {
new MediaType(mainType.toLowerCase, subType.toLowerCase))
}

implicit val http4sInstancesForMediaRange
: Show[MediaRange] with HttpCodec[MediaRange] with Order[MediaRange] =
new Show[MediaRange] with HttpCodec[MediaRange] with Order[MediaRange] {
override def show(s: MediaRange): String =
s"${s.mainType}/*${MediaRange.extensionsToString(s)}"

implicit val http4sShowForMediaRange: Show[MediaRange] =
Show.show(s => s"${s.mainType}/*${MediaRange.extensionsToString(s)}")
implicit val http4sOrderForMediaRange: Order[MediaRange] =
Order.from { (x, y) =>
def orderedSubtype(a: MediaRange) = a match {
case mt: MediaType => mt.subType
case _ => ""
}
def f(a: MediaRange) = (a.mainType, orderedSubtype(a), a.extensions.toVector.sortBy(_._1))
Order[(String, String, Vector[(String, String)])].compare(f(x), f(y))
}
implicit val http4sHttpCodecForMediaRange: HttpCodec[MediaRange] =
new HttpCodec[MediaRange] {
override def parse(s: String): ParseResult[MediaRange] =
MediaRange.parse(s)

override def render(writer: Writer, mr: MediaRange): writer.type = mr match {
case mt: MediaType => MediaType.http4sInstancesForMediaType.render(writer, mt)
case mt: MediaType => MediaType.http4sHttpCodecForMediaType.render(writer, mt)
case _ =>
writer << mr.mainType << "/*"
renderExtensions(writer, mr)
writer
}

override def compare(x: MediaRange, y: MediaRange): Int = {
def orderedSubtype(a: MediaRange) = a match {
case mt: MediaType => mt.subType
case _ => ""
}
def f(a: MediaRange) = (a.mainType, orderedSubtype(a), a.extensions.toVector.sortBy(_._1))
Order[(String, String, Vector[(String, String)])].compare(f(x), f(y))
}
}
}

Expand Down Expand Up @@ -264,12 +262,12 @@ object MediaType extends MimeDB {
new MediaType(mainType.toLowerCase, subType.toLowerCase))
}

implicit val http4sInstancesForMediaType
: Show[MediaType] with HttpCodec[MediaType] with Eq[MediaType] =
new Show[MediaType] with HttpCodec[MediaType] with Eq[MediaType] {
override def show(s: MediaType): String =
s"${s.mainType}/${s.subType}${MediaRange.extensionsToString(s)}"

implicit val http4sEqForMediaType: Eq[MediaType] =
Eq.fromUniversalEquals
implicit val http4sShowForMediaType: Show[MediaType] =
Show.show(s => s"${s.mainType}/${s.subType}${MediaRange.extensionsToString(s)}")
implicit val http4sHttpCodecForMediaType: HttpCodec[MediaType] =
new HttpCodec[MediaType] {
override def parse(s: String): ParseResult[MediaType] =
MediaType.parse(s)

Expand All @@ -278,9 +276,6 @@ object MediaType extends MimeDB {
MediaRange.renderExtensions(writer, mt)
writer
}

override def eqv(x: MediaType, y: MediaType): Boolean =
x.equals(y)
}

}
15 changes: 5 additions & 10 deletions core/src/main/scala/org/http4s/Method.scala
@@ -1,10 +1,10 @@
package org.http4s

import cats._
import cats.implicits._
import cats.{Eq, Show}
import org.http4s.Method.Semantics
import org.http4s.parser.Rfc2616BasicRules
import org.http4s.util.{Renderable, Writer}
import org.http4s.Method.Semantics

/**
* An HTTP method.
Expand All @@ -16,7 +16,7 @@ sealed abstract case class Method private (name: String) extends Renderable with
final override def render(writer: Writer): writer.type = writer << name
}

object Method extends MethodInstances {
object Method {
sealed trait Semantics {
def isIdempotent: Boolean
def isSafe: Boolean
Expand Down Expand Up @@ -144,12 +144,7 @@ object Method extends MethodInstances {
)

private val allByKey: Map[String, Right[Nothing, Method]] = all.map(m => (m.name, Right(m))).toMap
}

trait MethodInstances {
implicit val http4sEqForMethod: Eq[Method] =
Eq.fromUniversalEquals

implicit val http4sShowForMethod: Show[Method] =
Show.fromToString
implicit val http4sEqForMethod: Eq[Method] = Eq.fromUniversalEquals
implicit val http4sShowForMethod: Show[Method] = Show.fromToString
}