Created
June 10, 2016 09:04
-
-
Save privateblue/83ac22e2b1e23c4487babbc9939a3a23 to your computer and use it in GitHub Desktop.
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
import play.api.libs.json._ | |
/** | |
* IdT represents a typed unique identifier. The type of IdT is the type of the | |
* object that it points to. Thus an IdT[Blog] is the unique identifier of a blog. | |
* It is meant to be imported as import IdT => Id to replace old non-typed ids | |
* which are of type IdT[Nothing]. The covariance of IdT is only for backward | |
* compatibility. | |
*/ | |
class IdT[+A] private (val key: Long) extends AnyVal with Ordered[IdT[_]] with Serializable { | |
override def toString: String = key.toString | |
/** | |
* Returns this id as a base 36 number | |
*/ | |
def toShortString: String = BigInt(key).toString(36) | |
def canEqual(that: Any): Boolean = { | |
that match { | |
case t: IdT[_] => true | |
case l: Long => true | |
case _ => false | |
} | |
} | |
override def compare(that: IdT[_]): Int = | |
new IdT.IdTOrdering[Nothing].compare(new IdT[Nothing](this.key), new IdT[Nothing](that.key)) | |
/** | |
* Returns true if this id equals to Id(0), otherwise false | |
*/ | |
@inline final def isEmpty: Boolean = this == Id.empty | |
/** | |
* Returns true if this id is nonempty | |
*/ | |
@inline final def nonEmpty: Boolean = !isEmpty | |
/** | |
* Returns true if this id is nonempty | |
*/ | |
@inline final def isDefined: Boolean = !isEmpty | |
/** | |
* Returns this id if it is nonempty, otherwise throws a NoSuchElementException | |
*/ | |
@inline final def get: IdT[A] = if (isEmpty) throw new NoSuchElementException("Empty id") else this | |
/** | |
* Returns this id if it is nonempty, otherwise return the result of evaluating `default`. | |
*/ | |
@inline final def getOrElse(default: => IdT[_]): IdT[A] = if (isEmpty) new IdT[A](default.key) else this | |
/** | |
* Returns this id if it is nonempty, otherwise return the result of evaluating `default`. | |
*/ | |
@inline final def orElse(default: => IdT[_]): IdT[A] = if (isEmpty) new IdT[A](default.key) else this | |
/** | |
* Returns Some(this) if this id is nonempty, otherwise None | |
*/ | |
@inline final def toOption: Option[IdT[A]] = if (isEmpty) None else Some(this) | |
/** | |
* Returns List(this) if this id is nonempty, otherwise Nil | |
*/ | |
@inline final def toList: List[IdT[A]] = if (isEmpty) Nil else List(this) | |
/** | |
* Returns this id if it is nonempty and applying the predicate to it returns true, otherwise None | |
*/ | |
@inline final def filter(p: (IdT[A]) => Boolean): Option[IdT[A]] = if (!isEmpty && p(this)) Some(this) else None | |
/** | |
* Returns this id if it is nonempty and applying the predicate to it returns false, otherwise None | |
*/ | |
@inline final def filterNot(p: (IdT[A]) => Boolean): Option[IdT[A]] = if (!isEmpty && !p(this)) Some(this) else None | |
/** | |
* Applies the transformation if this id is nonempty, otherwise returns None | |
*/ | |
@inline final def map[B](f: (IdT[A]) => B): Option[B] = if (isEmpty) None else Some(f(this)) | |
/** | |
* Applies the transformation if this id is nonempty, otherwise returns None | |
*/ | |
@inline final def flatMap[B](f: (IdT[A]) => Option[B]): Option[B] = if (isEmpty) None else f(this) | |
// @deprecated("You should write Id as String instead", "") | |
@inline final def asNumber: Long = key | |
} | |
object IdT { | |
/** | |
* The empty Id | |
*/ | |
def empty[A]: IdT[A] = new IdT(0L) | |
/** | |
* Creates an Id from a Long | |
*/ | |
def apply[A](l: Long): IdT[A] = new IdT(l) | |
/** | |
* Creates an Id from a java.lang.Long | |
*/ | |
def apply[A](l: java.lang.Long): IdT[A] = new IdT(l.toLong) | |
/** | |
* Creates an Id from a String parsed as a Long | |
*/ | |
def apply[A](str: String): IdT[A] = new IdT(str.toLong) | |
/** | |
* Creates an Id from a BigInt | |
*/ | |
def apply[A](bi: BigInt): IdT[A] = new IdT(bi.toLong) | |
/** | |
* Returns the empty Id | |
*/ | |
def apply[A](): IdT[A] = empty | |
def unsafeCoerce[A, B](a: IdT[A]): IdT[B] = apply[B](a.key) | |
class IdTOrdering[A] extends Ordering[IdT[A]] { | |
def compare(x: IdT[A], y: IdT[A]): Int = | |
if (x.key < y.key) { | |
-1 | |
} else if (x.key == y.key) { | |
0 | |
} else { | |
1 | |
} | |
} | |
implicit def idTOrdering[A]: Ordering[IdT[A]] = | |
new IdTOrdering[A] | |
implicit def reads[A]: Reads[IdT[A]] = new Reads[IdT[A]] { | |
def reads(json: JsValue): JsResult[IdT[A]] = json match { | |
case JsNumber(n) => JsSuccess(IdT[A](n.toLong)) | |
case JsString(s) if s.nonEmpty && s.forall(_.isDigit) => JsSuccess(IdT[A](s)) | |
case _ => JsError(s"Invalid Id: $json") | |
} | |
} | |
implicit def idWrites[A]: Writes[IdT[A]] = new Writes[IdT[A]] { | |
def writes(id: IdT[A]): JsValue = JsString(id.toString) | |
} | |
implicit def toQueryParam[A]: ToQueryParam[IdT[A]] = ToQueryParam.instance[IdT[A]](_.toString) | |
implicit def toPathParam[A]: ToPathParam[IdT[A]] = ToPathParam.instance[IdT[A]](_.toString) | |
} | |
object Id { | |
/** | |
* The empty Id | |
*/ | |
val empty: Id = IdT.empty[Nothing] | |
/** | |
* Creates an Id from a Long | |
*/ | |
def apply(l: Long): Id = IdT[Nothing](l) | |
/** | |
* Creates an Id from a java.lang.Long | |
*/ | |
def apply(l: java.lang.Long): Id = IdT[Nothing](l.toLong) | |
/** | |
* Creates an Id from a String parsed as a Long | |
*/ | |
def apply(str: String): Id = IdT[Nothing](str.toLong) | |
/** | |
* Creates an Id from a BigInt | |
*/ | |
def apply(bi: BigInt): Id = IdT[Nothing](bi.toLong) | |
/** | |
* Returns the empty Id | |
*/ | |
def apply(): Id = empty | |
} | |
/** | |
* The class of traits which can be used as the value of a query parameter. | |
* This is usually equivalent to `scalaz.Show` or `Any` but unlike those, excludes | |
* types like `Post` and `Blog`. | |
* | |
* The `Option` type is also intentionally excluded to prevent bugs. | |
*/ | |
trait ToQueryParam[-A] { | |
/** | |
* Converts the object into a string representing its value as a query parameter. | |
* This string should not be percent-encoded (this is handled in ApiClient). | |
*/ | |
def toQueryParam(a: A): String | |
} | |
object ToQueryParam { | |
/** Acquire an instance of `ToQueryParam` for type `A`. */ | |
def apply[A](implicit ev: ToQueryParam[A]): ToQueryParam[A] = ev | |
/** | |
* Creates an instance of `ToQueryParam` for the type `A` | |
* using the given function as the value of `toQueryParam`. | |
*/ | |
def instance[A](f: A => String): ToQueryParam[A] = new ToQueryParam[A] { | |
def toQueryParam(a: A) = f(a) | |
} | |
implicit val shortToQueryParam: ToQueryParam[Short] = instance[Short](_.toString) | |
implicit val intToQueryParam: ToQueryParam[Int] = instance[Int](_.toString) | |
implicit val longToQueryParam: ToQueryParam[Long] = instance[Long](_.toString) | |
implicit val floatToQueryParam: ToQueryParam[Float] = instance[Float](_.toString) | |
implicit val doubleToQueryParam: ToQueryParam[Double] = instance[Double](_.toString) | |
implicit val charToQueryParam: ToQueryParam[Char] = instance[Char](_.toString) | |
implicit val stringToQueryParam: ToQueryParam[String] = instance[String](s => s) | |
implicit val byteToQueryParam: ToQueryParam[Byte] = instance[Byte](_.toString) | |
implicit val booleanToQueryParam: ToQueryParam[Boolean] = instance[Boolean](_.toString) | |
object Implicits extends ToQueryParamImplicits | |
} | |
/** Provides methods for `ToQueryParam`s. */ | |
trait ToQueryParamImplicits { | |
implicit class ToQueryParamOps[A: ToQueryParam](a: A) { | |
def toQueryParam: String = implicitly[ToQueryParam[A]].toQueryParam(a) | |
} | |
} | |
/** | |
* The class of traits which can be used as the value of a path segment. | |
* This is usually equivalent to `scalaz.Show` or `Any` but unlike those, excludes | |
* types like `Post` and `Blog`. | |
* | |
* The `Option` type is also intentionally excluded to prevent bugs. | |
*/ | |
trait ToPathParam[-A] { | |
/** | |
* Converts the object into a string representing its value as a path segment. | |
* This string should not be percent-encoded (this is handled in ApiClient). | |
*/ | |
def toPathParam(a: A): String | |
} | |
object ToPathParam { | |
/** Acquire an instance of `ToPathParam` for type `A`. */ | |
def apply[A](implicit ev: ToPathParam[A]): ToPathParam[A] = ev | |
/** | |
* Creates an instance of `ToPathParam` for the type `A` | |
* using the given function as the value of `toPathParam`. | |
*/ | |
def instance[A](f: A => String): ToPathParam[A] = new ToPathParam[A] { | |
def toPathParam(a: A) = f(a) | |
} | |
implicit val shortToPathParam: ToPathParam[Short] = instance[Short](_.toString) | |
implicit val intToPathParam: ToPathParam[Int] = instance[Int](_.toString) | |
implicit val longToPathParam: ToPathParam[Long] = instance[Long](_.toString) | |
implicit val floatToPathParam: ToPathParam[Float] = instance[Float](_.toString) | |
implicit val doubleToPathParam: ToPathParam[Double] = instance[Double](_.toString) | |
implicit val charToPathParam: ToPathParam[Char] = instance[Char](_.toString) | |
implicit val stringToPathParam: ToPathParam[String] = instance[String](s => s) | |
implicit val byteToPathParam: ToPathParam[Byte] = instance[Byte](_.toString) | |
implicit val booleanToPathParam: ToPathParam[Boolean] = instance[Boolean](_.toString) | |
object Implicits extends ToPathParamImplicits | |
} | |
/** Provides methods for `ToPathParam`s. */ | |
trait ToPathParamImplicits { | |
implicit class ToPathParamOps[A: ToPathParam](a: A) { | |
def toPathParam: String = implicitly[ToPathParam[A]].toPathParam(a) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment