package net.liftweb
package common

// WHOA NELLY
// Slightly redefined HLists (we want this in Lift). This allows HLists to be
// covariant in their parameters, which makes things work properly.

object HListies {

  /**
   * The trait that defines HLists
   */
  sealed trait HList

  implicit final class HListMethods[ListSoFar <: HList](hlist: ListSoFar) extends AnyRef {
    def :+:[T](v: T): :+:[T, ListSoFar] = {
      HListies.:+:(v, hlist)
    }

    def length: Int = {
      hlist match {
        case HNil =>
          0
        case head :+: rest =>
          1 + rest.length
      }
    }
  }

  /**
   * The last element of an HList
   */
  final class HNil extends HList {
    override def toString = "HNil"
  }

  /**
   * The HNil singleton
   */
  val HNil = new HNil()

  /**
   * The HList cons cell
   */
  final case class :+:[+H, +T <: HList](head: H, tail: T) extends HList {
    override def toString = head + " :+: " + tail
  }

  //type :+:[+H, +T <: HList] = HCons[H, T]

  /*object :+: {
    def unapply[H, T <: HList](in: HCons[H, T]): Option[(H, T)] = Some(in.head, in.tail)
  }*/

}

package net.liftweb
package common

// WHOA NELLY
// Slightly redefined HLists (we want this in Lift). This allows HLists to be
// covariant in their parameters, which makes things work properly.

object HListies {

  /**
   * The trait that defines HLists
   */
  sealed trait HList

  implicit final class HListMethods[ListSoFar <: HList](hlist: ListSoFar) extends AnyRef {
    def :+:[T](v: T): :+:[T, ListSoFar] = {
      HListies.:+:(v, hlist)
    }

    def length: Int = {
      if (hlist == HNil) {
        0
      } else {
        hlist match {
          case head :+: rest =>
            1 + rest.length
        }
      }
    }
  }

  /**
   * The HNil singleton
   */
  final object HNil extends HList {
    override def toString = "HNil"
  }
  type HNil = HNil.type

  /**
   * The HList cons cell
   */
  final case class :+:[+H, +T <: HList](head: H, tail: T) extends HList {
    override def toString = head + " :+: " + tail
  }

  //type :+:[+H, +T <: HList] = HCons[H, T]

  /*object :+: {
    def unapply[H, T <: HList](in: HCons[H, T]): Option[(H, T)] = Some(in.head, in.tail)
  }*/

}

Rework how we represent HNil.
Rework how we represent HNil.

Scala 2.12 has some tweaked exhaustiveness checks on pattern matching that
catch a possibility we hadn't aniticipated. In particular, HNil was defined as
a final class, and we created a singleton instance of it to reference, but a
user could instantiate the final class themselves and send that in and wreak
havoc.

We redefine HNil as a final singleton object, and define a type HNil that is
equivalent to the singleton's type so we can reference it as both a value and
type called HNil. Other code remains unchanged, fortunately.

Unfortunately, this means we have to keep using our forkity fork of Lift's
HLists... Hopefully a later Lift minor version can include these tweaks, but
we'll need to maintain our fork until we no longer support versions of Lift
with the old implementation.
