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

replpp.shaded.mainargs.Invoker.scala Maven / Gradle / Ivy

There is a newer version: 0.1.98
Show newest version
package replpp.shaded.mainargs

object Invoker {
  def construct[T](
      cep: TokensReader.Class[T],
      args: Seq[String],
      allowPositional: Boolean,
      allowRepeats: Boolean
  ): Result[T] = {
    TokenGrouping
      .groupArgs(
        args,
        cep.main.flattenedArgSigs,
        allowPositional,
        allowRepeats,
        cep.main.argSigs0.exists(_.reader.isLeftover)
      )
      .flatMap((group: TokenGrouping[Any]) => invoke(cep.companion(), cep.main, group))
  }

  def invoke0[T, B](
      base: B,
      mainData: MainData[T, B],
      kvs: Map[ArgSig, Seq[String]],
      extras: Seq[String]
  ): Result[T] = {
    val readArgValues: Seq[Either[Result[Any], ParamResult[_]]] =
      for (a <- mainData.argSigs0) yield {
        a.reader match {
          case r: TokensReader.Flag =>
            Right(ParamResult.Success(Flag(kvs.contains(a)).asInstanceOf[T]))
          case r: TokensReader.Simple[T] => Right(makeReadCall(kvs, base, a, r))
          case r: TokensReader.Constant[T] => Right(r.read() match {
            case Left(s) => ParamResult.Failure(Seq(Result.ParamError.Failed(a, Nil, s)))
            case Right(v) => ParamResult.Success(v)
          })
          case r: TokensReader.Leftover[T, _] => Right(makeReadVarargsCall(a, extras, r))
          case r: TokensReader.Class[T] =>
            Left(
              invoke0[T, B](
                r.companion().asInstanceOf[B],
                r.main.asInstanceOf[MainData[T, B]],
                kvs,
                extras
              )
            )

        }
      }

    val validated = {
      val lefts = readArgValues
        .collect {
          case Left(Result.Failure.InvalidArguments(lefts)) => lefts
          case Right(ParamResult.Failure(failure)) => failure
        }
        .flatten
      if (lefts.nonEmpty) Result.Failure.InvalidArguments(lefts)
      else Result.Success(
        readArgValues.collect {
          case Left(Result.Success(x)) => x
          case Right(ParamResult.Success(x)) => x
        }
      )
    }

    val res = validated.flatMap { validated =>
      Result.Success(mainData.invokeRaw(base, validated))
    }
    res
  }
  def invoke[T, B](target: B, main: MainData[T, B], grouping: TokenGrouping[B]): Result[T] = {
    try invoke0(
        target,
        main,
        grouping.grouped,
        grouping.remaining
      )
    catch { case e: Throwable => Result.Failure.Exception(e) }
  }
  def runMains[B](
      mains: MethodMains[B],
      args: Seq[String],
      allowPositional: Boolean,
      allowRepeats: Boolean
  ): Either[Result.Failure.Early, (MainData[Any, B], Result[Any])] = {
    def groupArgs(main: MainData[Any, B], argsList: Seq[String]) = {
      def invokeLocal(group: TokenGrouping[Any]) =
        invoke(mains.base(), main.asInstanceOf[MainData[Any, Any]], group)
      Right(
        main,
        TokenGrouping
          .groupArgs(
            argsList,
            main.flattenedArgSigs,
            allowPositional,
            allowRepeats,
            main.argSigs0.exists {
              case x: ArgSig => x.reader.isLeftover
              case _ => false
            }
          )
          .flatMap(invokeLocal)
      )
    }
    mains.value match {
      case Seq() => Left(Result.Failure.Early.NoMainMethodsDetected())
      case Seq(main) => groupArgs(main, args)
      case multiple =>
        args.toList match {
          case List() => Left(Result.Failure.Early.SubcommandNotSpecified(multiple.map(_.name)))
          case head :: tail =>
            if (head.startsWith("-")) {
              Left(Result.Failure.Early.SubcommandSelectionDashes(head))
            } else {
              multiple.find(_.name == head) match {
                case None =>
                  Left(Result.Failure.Early.UnableToFindSubcommand(multiple.map(_.name), head))
                case Some(main) => groupArgs(main, tail)
              }
            }
        }
    }
  }

  def tryEither[T](t: => T, error: Throwable => Result.ParamError): Either[Result.ParamError, T] = {
    try Right(t)
    catch { case e: Throwable => Left(error(e)) }
  }
  def makeReadCall[T](
      dict: Map[ArgSig, Seq[String]],
      base: Any,
      arg: ArgSig,
      reader: TokensReader.Simple[_]
  ): ParamResult[T] = {
    def prioritizedDefault = tryEither(
      arg.default.map(_(base)),
      Result.ParamError.DefaultFailed(arg, _)
    ) match {
      case Left(ex) => ParamResult.Failure(Seq(ex))
      case Right(v) => ParamResult.Success(v)
    }
    val tokens = dict.get(arg) match {
      case None => if (reader.allowEmpty) Some(Nil) else None
      case Some(tokens) => Some(tokens)
    }
    val optResult = tokens match {
      case None => prioritizedDefault
      case Some(tokens) =>
        tryEither(
          reader.read(tokens),
          Result.ParamError.Exception(arg, tokens, _)
        ) match {
          case Left(ex) => ParamResult.Failure(Seq(ex))
          case Right(Left(errMsg)) =>
            ParamResult.Failure(Seq(Result.ParamError.Failed(arg, tokens, errMsg)))
          case Right(Right(v)) => ParamResult.Success(Some(v))
        }
    }
    optResult.map(_.get.asInstanceOf[T])
  }

  def makeReadVarargsCall[T](
      arg: ArgSig,
      values: Seq[String],
      reader: TokensReader.Leftover[_, _]
  ): ParamResult[T] = {
    val eithers =
      tryEither(
        reader.read(values),
        Result.ParamError.Exception(arg, values, _)
      ) match {
        case Left(x) => Left(x)
        case Right(Left(errMsg)) => Left(Result.ParamError.Failed(arg, values, errMsg))
        case Right(Right(v)) => Right(v)
      }

    eithers match {
      case Left(s) => ParamResult.Failure(Seq(s))
      case Right(v) => ParamResult.Success(v.asInstanceOf[T])
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy