All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spinoco.protocol.http.header.value.HttpMediaRange.scala Maven / Gradle / Ivy

The newest version!
package spinoco.protocol.http.header.value

import scodec.codecs._
import scodec.{Attempt, Codec, Err}
import spinoco.protocol.common.Terminator
import spinoco.protocol.http.codec.helper._
import spinoco.protocol.mime.MediaType
import spinoco.protocol.common.codec._

sealed trait HttpMediaRange { self =>
  import HttpMediaRange._

  def qValue: Option[Float]

  def params: Map[String, String]

  def acceptParams: Map[String, String]

  def updateQValue(qValue:Option[Float]): HttpMediaRange = self match {
    case pattern: Pattern => pattern.copy(qValue = qValue)
    case one: One => one.copy(qValue = qValue)
  }

  def updateParams(params: Map[String, String]): HttpMediaRange = self match {
    case pattern: Pattern => pattern.copy(params = params)
    case one: One => one.copy(params = params)
  }

  def updateAcceptParams(params: Map[String, String]): HttpMediaRange = self match {
    case pattern: Pattern => pattern.copy(acceptParams = params)
    case one: One => one.copy(acceptParams = params)
  }

}


object HttpMediaRange {

  sealed case class Pattern(
    mainType: String
    , qValue: Option[Float]
    , params: Map[String, String] = Map.empty
    , acceptParams: Map[String, String]  = Map.empty
  ) extends HttpMediaRange

  sealed case class One(
    mediaType: MediaType
    , qValue: Option[Float]
    , params: Map[String, String] = Map.empty
    , acceptParams: Map[String, String] = Map.empty
  )  extends HttpMediaRange

  val `*/*` = Pattern("*", None)
  val `application/*` = Pattern("application", None)
  val `audio/*` = Pattern("audio", None)
  val `image/*` = Pattern("image", None)
  val `message/*` = Pattern("message", None)
  val `multipart/*` = Pattern("multipart", None)
  val `text/*` = Pattern("text", None)
  val `video/*` = Pattern("video", None)




  val codec : Codec[HttpMediaRange] = {

    val parameter: Codec[(String, String)] = {
      terminated(trimmedAsciiToken, Terminator.constantString1("=")) ~ trimmedAsciiToken
    }

    val wildcardCodec: Codec[Pattern] = {
      asciiStringNoWs.exmap(
        s => {
          if (s == "*") Attempt.successful(Pattern("*", None)) else
          Attempt.failure(Err(s"Expected wildcard pattern (*), got $s"))
        }
        , p => {
          if (p.mainType == "*") Attempt.successful("*")
          else Attempt.failure(Err(s"Expected wildcard pattern, got $p"))
        }

      )
    }

    val patternCodec: Codec[Pattern] = {
      (terminated(trimmedAsciiToken, Terminator.constantString1("/")) ~ trimmedAsciiToken)
      .exmap(
        {
          case (tpe, "*") => Attempt.successful(Pattern(tpe, None))
          case (tpe, sub) => Attempt.failure(Err(s"Expected wildcard pattern, got $tpe/$sub"))
        }
        , p => Attempt.successful(p.mainType -> "*")
      )
    }

    val patternOrMedia: Codec[HttpMediaRange] = {
      choice(
        patternCodec.upcast
        , wildcardCodec.upcast
        , MediaType.codec.xmap[One](One(_,None),  _.mediaType).upcast
      )
    }

    val patterMediaWithParams: Codec[HttpMediaRange] = {
      parametrized(semicolon, patternOrMedia <~ ignoreWS, delimitedBy(semicolon, semicolon, parameter)).xmap(
        {case (mtr, pars) => mtr.updateParams(pars.toList.flatten.toMap)}
        , mtr => (mtr, mtr.params.headOption.map(_ => mtr.params.toList))
      )
    }

    val qAccepts = {
      floatAsString ~ maybe(ignoreWS ~> constant(semicolon) ~> delimitedBy(semicolon, semicolon, parameter))
    }


    choice(
      terminated(patterMediaWithParams, Terminator.constant1((semicolon ++ qPar).bits)) ~ qAccepts.widen[Option[(Float, Option[List[(String, String)]])]](a => Some(a), {
        case None => Attempt.failure(Err("---"))
        case Some(v) => Attempt.successful(v)
      })
      , terminated(patterMediaWithParams, Terminator.constant1((semicolon_SP ++ qPar).bits)) ~ qAccepts.widen[Option[(Float, Option[List[(String, String)]])]](a => Some(a), {
        case None => Attempt.failure(Err("---"))
        case Some(v) => Attempt.successful(v)
      })
      , patterMediaWithParams.xmap[(HttpMediaRange, Option[(Float, Option[List[(String, String)]])])](mtr => mtr -> None, {case (mtr, _) => mtr})
    ).xmap[HttpMediaRange](
      { case (mtr, qv) => mtr.updateQValue(qv.map(_._1)).updateAcceptParams(qv.flatMap(_._2).toList.flatten.toMap) }
      , mtr => mtr -> mtr.qValue.map(_ -> mtr.acceptParams.headOption.map(_ => mtr.acceptParams.toList))
    )
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy