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

caseapp.core.HListParser.scala Maven / Gradle / Ivy

package caseapp
package core

import shapeless._
import shapeless.labelled.{ FieldType, field }

import caseapp.util.Implicit

trait HListParser[L <: HList, D <: HList, -N <: HList, -V <: HList, -M <: HList, -H <: HList, R <: HList] {
  type P <: HList
  def apply(default: D, names: N, valueDescriptions: V, helpMessages: M, noHelp: H): Parser.Aux[L, P]
}

object HListParser {
  def apply[L <: HList, D <: HList, N <: HList, V <: HList, M <: HList, H <: HList, R <: HList](implicit args: HListParser[L, D, N, V, M, H, R]): Aux[L, D, N, V, M, H, R, args.P] = args

  type Aux[L <: HList, D <: HList, N <: HList, V <: HList, M <: HList, H <: HList, R <: HList, P0 <: HList] =
    HListParser[L, D, N, V, M, H, R] { type P = P0 }

  def instance[L <: HList, D <: HList, N <: HList, V <: HList, M <: HList, H <: HList, R <: HList, P0 <: HList](p: (D, N, V, M, H) => Parser.Aux[L, P0]): Aux[L, D, N, V, M, H, R, P0] =
    new HListParser[L, D, N, V, M, H, R] {
      type P = P0
      def apply(default: D, names: N, valueDescriptions: V, helpMessages: M, noHelp: H) = p(default, names, valueDescriptions, helpMessages, noHelp)
    }

  implicit val hnil: Aux[HNil, HNil, HNil, HNil, HNil, HNil, HNil, HNil] =
    instance { (_, _, _, _, _) =>
      new Parser[HNil] {
        type D = HNil
        def init = HNil
        def step(args: Seq[String], d: HNil) = Right(None)
        def get(d: HNil) = Right(HNil)
        def args = Vector.empty
      }
    }

  implicit def hconsTaggedDefault[K <: Symbol, Tag, H, T <: HList, PT <: HList, DT <: HList, NT <: HList, VT <: HList, MT <: HList, HT <: HList, RT <: HList]
   (implicit
    name: Witness.Aux[K],
    argParser: Strict[ArgParser[H @@ Tag]],
    headDefault: Implicit[Option[Default[H @@ Tag]]],
    tail: Strict[Aux[T, DT, NT, VT, MT, HT, RT, PT]]
   ): Aux[FieldType[K, H @@ Tag] :: T, Option[H @@ Tag] :: DT, List[Name] :: NT, Option[ValueDescription] :: VT, Option[HelpMessage] :: MT, Option[Hidden] :: HT, None.type :: RT, Option[H @@ Tag] :: PT] =
    hconsDefault[K, H @@ Tag, T, PT, DT, NT, VT, MT, HT, RT]

  implicit def hconsDefault[K <: Symbol, H, T <: HList, PT <: HList, DT <: HList, NT <: HList, VT <: HList, MT <: HList, HT <: HList, RT <: HList]
   (implicit
    name: Witness.Aux[K],
    argParser: Strict[ArgParser[H]],
    headDefault: Implicit[Option[Default[H]]],
    tail: Strict[Aux[T, DT, NT, VT, MT, HT, RT, PT]]
   ): Aux[FieldType[K, H] :: T, Option[H] :: DT, List[Name] :: NT, Option[ValueDescription] :: VT, Option[HelpMessage] :: MT, Option[Hidden] :: HT, None.type :: RT, Option[H] :: PT] =
    instance { (default0, names, valueDescriptions, helpMessages, noHelp) =>
      val tailParser = tail.value(default0.tail, names.tail, valueDescriptions.tail, helpMessages.tail, noHelp.tail)
      val headNames = Name(name.value.name) :: names.head
      val headDescriptions = valueDescriptions.head
      val headDefault0 = default0.head

      new Parser[FieldType[K, H] :: T] {
        val args =
          Arg(name.value.name, headNames, headDescriptions, helpMessages.head, noHelp.head.nonEmpty, argParser.value.isFlag) +: tailParser.args

        type D = Option[H] :: PT
        def init = None :: tailParser.init
        def step(args: Seq[String], d: Option[H] :: PT) = {
          if (args.isEmpty)
            Right(None)
          else {
            val matchedOpt = headNames.iterator.map(_.apply(args.head)).collectFirst {
              case Right(valueOpt) => valueOpt
            }

            matchedOpt match {
              case Some(valueOpt) =>
                if (valueOpt.isEmpty && args.tail.isEmpty)
                  argParser.value(d.head).right.map(h => Some((Some(h) :: d.tail, args.tail)))
                else
                  argParser.value(d.head, valueOpt.getOrElse(args.tail.head), valueOpt.nonEmpty).right.flatMap {
                    case (usedArg, h) =>
                      if (valueOpt.nonEmpty && !usedArg)
                        Left(s"Unrecognized value: ${valueOpt.get}")
                      else
                        Right(Some((Some(h) :: d.tail, if (valueOpt.nonEmpty) args.tail else if (usedArg) args.tail.tail else args.tail)))
                  }

              case None =>
                tailParser.step(args, d.tail).right.map(_.map {
                  case (t, args) => (d.head :: t, args)
                })
            }
          }
        }
        def get(d: Option[H] :: PT) = {
          val maybeHead = d.head
            .orElse(headDefault0)
            .orElse(headDefault.value.map(_()))
            .toRight(s"Required option ${name.value.name} / $headNames not specified")
          for {
            h <- maybeHead.right
            t <- tailParser.get(d.tail).right
          } yield field[K](h) :: t
        }
      }
    }

  implicit def hconsRecursive[K <: Symbol, H, HD, T <: HList, PT <: HList, DT <: HList, NT <: HList, VT <: HList, MT <: HList, HT <: HList, RT <: HList]
   (implicit
     headParser: Strict[Parser.Aux[H, HD]],
     tail: Aux[T, DT, NT, VT, MT, HT, RT, PT]
   ): Aux[FieldType[K, H] :: T, Option[H] :: DT, Nil.type :: NT, None.type :: VT, None.type :: MT, None.type :: HT, Some[Recurse] :: RT, HD :: PT] =
    instance { (default0, names, valueDescriptions, helpMessages, noHelp) =>
      val tailParser = tail(default0.tail, names.tail, valueDescriptions.tail, helpMessages.tail, noHelp.tail)

      new Parser[FieldType[K, H] :: T] {
        val args = headParser.value.args ++ tailParser.args
        type D = HD :: PT
        def init = headParser.value.init :: tailParser.init
        def step(args: Seq[String], d: HD :: PT) =
          headParser.value.step(args, d.head).right.flatMap {
            case None =>
              tailParser.step(args, d.tail).right.map(_.map {
                case (t, args) => (d.head :: t, args)
              })
            case Some((h, args)) =>
              Right(Some(h :: d.tail, args))
          }
        def get(d: HD :: PT) =
          for {
            h <- headParser.value.get(d.head).right
            t <- tailParser.get(d.tail).right
          } yield field[K](h) :: t
      }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy