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

caseapp.core.parser.ConsParser.scala Maven / Gradle / Ivy

There is a newer version: 2.0.0-M8
Show newest version
package caseapp.core.parser

import caseapp.core.argparser.{ArgParser, Consumed}
import caseapp.core.{Arg, Error}
import caseapp.core.util.NameOps.toNameOps
import shapeless.{:: => :*:, HList}

final case class ConsParser[H, T <: HList, DT <: HList](
  arg: Arg,
  argParser: ArgParser[H],
  default: Option[H],
  tail: Parser.Aux[T, DT]
) extends Parser[H :*: T] {

  type D = Option[H] :*: DT

  def init: D =
    None :: tail.init

  def step(args: List[String], d: Option[H] :*: tail.D): Either[(Error, List[String]), Option[(D, List[String])]] =
    args match {
      case Nil =>
        Right(None)

      case firstArg :: rem =>
        val matchedOpt = (Iterator(arg.name) ++ arg.extraNames.iterator)
          .map(n => n -> n(firstArg))
          .collectFirst {
            case (n, Right(valueOpt)) => n -> valueOpt
          }

        matchedOpt match {
          case Some((name, valueOpt)) =>

            val (res, rem0) = valueOpt match {
              case Some(value) =>
                val res0 = argParser(d.head, value)
                  .right
                  .map(h => Some(Some(h) :: d.tail))
                (res0, rem)
              case None =>
                rem match {
                  case Nil =>
                    val res0 = argParser(d.head)
                      .right
                      .map(h => Some(Some(h) :: d.tail))
                    (res0, Nil)
                  case th :: tRem =>
                    val (Consumed(usedArg), res) = argParser.optional(d.head, th)
                    val res0 = res.right.map(h => Some(Some(h) :: d.tail))
                    (res0, if (usedArg) tRem else rem)
                }
            }

            res
              .left
              .map { err =>
                (Error.ParsingArgument(name, err), rem0)
              }
              .right
              .map(_.map((_, rem0)))

          case None =>
            tail
              .step(args, d.tail)
              .right
              .map(_.map {
                case (t, args) => (d.head :: t, args)
              })
        }
    }

  def get(d: D): Either[Error, H :*: T] = {

    val maybeHead = d.head
      .orElse(default)
      .toRight {
        Error.RequiredOptionNotSpecified(
          arg.name.option,
          arg.extraNames.map(_.option)
        )
      }

    val maybeTail = tail.get(d.tail)

    (maybeHead, maybeTail) match {
      case (Left(headErr), Left(tailErrs)) => Left(headErr.append(tailErrs))
      case (Left(headErr), _) => Left(headErr)
      case (_, Left(tailErrs)) => Left(tailErrs)
      case (Right(h), Right(t)) => Right(h :: t)
    }
  }

  val args: Seq[Arg] =
    arg +: tail.args

  def mapHead[I](f: H => I): Parser.Aux[I :*: T, D] =
    map { l =>
      f(l.head) :: l.tail
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy