Skip to content

Instantly share code, notes, and snippets.

@privateblue
Created June 10, 2016 09:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save privateblue/83ac22e2b1e23c4487babbc9939a3a23 to your computer and use it in GitHub Desktop.
Save privateblue/83ac22e2b1e23c4487babbc9939a3a23 to your computer and use it in GitHub Desktop.
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