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

spray.httpx.marshalling.Marshaller.scala Maven / Gradle / Ivy

/*
 * Copyright © 2011-2015 the spray project 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package spray.httpx.marshalling

import scala.util.control.NonFatal
import akka.actor.ActorRef
import spray.http._

//# source-quote
trait Marshaller[-T] {
  def apply(value: T, ctx: MarshallingContext)
}
//#

object Marshaller extends BasicMarshallers
    with MetaMarshallers
    with MultipartMarshallers {

  def apply[T](f: (T, MarshallingContext) ⇒ Unit): Marshaller[T] =
    new Marshaller[T] {
      def apply(value: T, ctx: MarshallingContext): Unit =
        try f(value, ctx)
        catch {
          case NonFatal(e) ⇒ ctx.handleError(e)
        }
    }

  def of[T](marshalTo: ContentType*)(f: (T, ContentType, MarshallingContext) ⇒ Unit): Marshaller[T] =
    new Marshaller[T] {
      def apply(value: T, ctx: MarshallingContext): Unit =
        try {
          ctx.tryAccept(marshalTo) match {
            case Some(contentType) ⇒ f(value, contentType, ctx)
            case None              ⇒ ctx.rejectMarshalling(marshalTo)
          }
        } catch {
          case NonFatal(e) ⇒ ctx.handleError(e)
        }
    }

  def delegate[A, B](marshalTo: ContentType*) = new MarshallerDelegation[A, B](marshalTo)

  class MarshallerDelegation[A, B](marshalTo: Seq[ContentType]) {
    def apply(f: A ⇒ B)(implicit mb: Marshaller[B]): Marshaller[A] = apply((a, ct) ⇒ f(a))
    def apply(f: (A, ContentType) ⇒ B)(implicit mb: Marshaller[B]): Marshaller[A] =
      Marshaller.of[A](marshalTo: _*) { (value, contentType, ctx) ⇒
        mb(f(value, contentType), ctx.withContentTypeOverriding(contentType))
      }
  }
}

trait ToResponseMarshaller[-T] {
  def apply(value: T, ctx: ToResponseMarshallingContext)

  def compose[U](f: U ⇒ T): ToResponseMarshaller[U] = ToResponseMarshaller((value, ctx) ⇒ apply(f(value), ctx))
}

object ToResponseMarshaller extends BasicToResponseMarshallers
    with MetaToResponseMarshallers
    with LowPriorityToResponseMarshallerImplicits {
  def fromMarshaller[T](status: StatusCode = StatusCodes.OK, headers: Seq[HttpHeader] = Nil)(implicit m: Marshaller[T]): ToResponseMarshaller[T] =
    fromStatusCodeAndHeadersAndT.compose(t ⇒ (status, headers, t))

  def apply[T](f: (T, ToResponseMarshallingContext) ⇒ Unit): ToResponseMarshaller[T] =
    new ToResponseMarshaller[T] {
      def apply(value: T, ctx: ToResponseMarshallingContext): Unit =
        try f(value, ctx)
        catch {
          case NonFatal(e) ⇒ ctx.handleError(e)
        }
    }

  def of[T](marshalTo: ContentType*)(f: (T, ContentType, ToResponseMarshallingContext) ⇒ Unit): ToResponseMarshaller[T] =
    new ToResponseMarshaller[T] {
      def apply(value: T, ctx: ToResponseMarshallingContext): Unit =
        try {
          ctx.tryAccept(marshalTo) match {
            case Some(contentType) ⇒ f(value, contentType, ctx)
            case None              ⇒ ctx.rejectMarshalling(marshalTo)
          }
        } catch {
          case NonFatal(e) ⇒ ctx.handleError(e)
        }
    }

  def oneOf[T](marshalTo: ContentType*)(marshallers: ToResponseMarshaller[T]*): ToResponseMarshaller[T] =
    ToResponseMarshaller.of[T](marshalTo: _*) { (t, tpe, ctx) ⇒
      def tryNext(marshallers: List[ToResponseMarshaller[T]], previouslySupported: Set[ContentType]): Unit = marshallers match {
        case head :: tail ⇒
          head(t, new ToResponseMarshallingContext {
            def tryAccept(contentTypes: Seq[ContentType]): Option[ContentType] = ctx.tryAccept(contentTypes)
            def rejectMarshalling(supported: Seq[ContentType]): Unit = tryNext(tail, previouslySupported ++ supported)
            def marshalTo(response: HttpResponse): Unit = ctx.marshalTo(response)
            def handleError(error: Throwable): Unit = ctx.handleError(error)
            def startChunkedMessage(response: HttpResponse, ack: Option[Any])(implicit sender: ActorRef): ActorRef =
              ctx.startChunkedMessage(response, ack)(sender)
          })
        case Nil ⇒ ctx.rejectMarshalling(previouslySupported.toSeq)
      }
      tryNext(marshallers.toList, Set.empty)
    }

  def delegate[A, B](marshalTo: ContentType*) = new MarshallerDelegation[A, B](marshalTo)

  class MarshallerDelegation[A, B](marshalTo: Seq[ContentType]) {
    def apply(f: A ⇒ B)(implicit mb: ToResponseMarshaller[B]): ToResponseMarshaller[A] = apply((a, ct) ⇒ f(a))
    def apply(f: (A, ContentType) ⇒ B)(implicit mb: ToResponseMarshaller[B]): ToResponseMarshaller[A] =
      ToResponseMarshaller.of[A](marshalTo: _*) { (value, contentType, ctx) ⇒
        mb(f(value, contentType), ctx.withContentTypeOverriding(contentType))
      }
  }
}

sealed trait LowPriorityToResponseMarshallerImplicits {
  implicit def liftMarshallerConversion[T](m: Marshaller[T]): ToResponseMarshaller[T] = liftMarshaller(m)
  implicit def liftMarshaller[T](implicit m: Marshaller[T]): ToResponseMarshaller[T] =
    ToResponseMarshaller.fromMarshaller()
}

/** Something that can later be marshalled into a response */
trait ToResponseMarshallable {
  def marshal(ctx: ToResponseMarshallingContext): Unit
}
object ToResponseMarshallable {
  implicit def isMarshallable[T](value: T)(implicit marshaller: ToResponseMarshaller[T]): ToResponseMarshallable =
    new ToResponseMarshallable {
      def marshal(ctx: ToResponseMarshallingContext): Unit = marshaller(value, ctx)
    }
  implicit def marshallableIsMarshallable: ToResponseMarshaller[ToResponseMarshallable] =
    new ToResponseMarshaller[ToResponseMarshallable] {
      def apply(value: ToResponseMarshallable, ctx: ToResponseMarshallingContext): Unit = value.marshal(ctx)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy