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

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

package caseapp
package core

trait ArgParser[T] {
  def apply(current: Option[T], s: String, mandatory: Boolean): Either[String, (Boolean, T)]
  def apply(current: Option[T]): Either[String, T]
  def isFlag: Boolean = false
}

object ArgParser extends PlatformArgParsers {
  def apply[T](implicit parser: ArgParser[T]): ArgParser[T] = parser

  def instance[T](f: String => Either[String, T]): ArgParser[T] =
    new ArgParser[T] {
      def apply(current: Option[T], s: String, mandatory: Boolean) = f(s).right.map((true, _))
      def apply(current: Option[T]) = Left("argument missing")
    }

  def flag[T](f: Option[String] => Either[String, T]): ArgParser[T] =
    new ArgParser[T] {
      def apply(current: Option[T], s: String, mandatory: Boolean) =
        f(if (mandatory) Some(s) else None).right.map((mandatory, _))
      def apply(current: Option[T]) = f(None)
      override def isFlag = true
    }

  def accumulator[T](f: (Option[T], String) => Either[String, T]): ArgParser[T] =
    new ArgParser[T] {
      def apply(current: Option[T], s: String, mandatory: Boolean) =
        f(current, s).right.map((true, _))
      def apply(current: Option[T]) = Left("argument missing")
    }

  def flagAccumulator[T](f: (Option[T], Option[String]) => Either[String, T]): ArgParser[T] =
    new ArgParser[T] {
      def apply(current: Option[T], s: String, mandatory: Boolean) =
        f(current, if (mandatory) Some(s) else None).right.map((mandatory, _))
      def apply(current: Option[T]) = f(current, None)
      override def isFlag = true
    }

  implicit def int: ArgParser[Int] =
    instance { s =>
      try Right(s.toInt)
      catch { case _: NumberFormatException =>
        Left(s"Malformed integer: $s")
      }
    }
  implicit def long: ArgParser[Long] =
    instance { s =>
      try Right(s.toLong)
      catch { case _: NumberFormatException =>
        Left(s"Malformed long integer: $s")
      }
    }
  implicit def double: ArgParser[Double] =
    instance { s =>
      try Right(s.toDouble)
      catch { case _: NumberFormatException =>
        Left(s"Malformed double float: $s")
      }
    }
  implicit def float: ArgParser[Float] =
    instance { s =>
      try Right(s.toFloat)
      catch { case _: NumberFormatException =>
        Left(s"Malformed float: $s")
      }
    }
  implicit def bigDecimal: ArgParser[BigDecimal] =
    instance { s =>
      try Right(BigDecimal(s))
      catch { case _: NumberFormatException =>
        Left(s"Malformed decimal: $s")
      }
    }
  implicit def string: ArgParser[String] =
    instance(Right(_))
  implicit def unit: ArgParser[Unit] = {
    val trues = Set("true", "1")
    val falses = Set("false", "0")

    flag {
      case None => Right(())
      case Some(s) =>
        if (trues(s))
          Right(())
        else if (falses(s))
          Left(s"Option cannot be explicitly disabled")
        else
          Left(s"Unrecognized flag value: $s")
    }
  }
  implicit def boolean: ArgParser[Boolean] = {
    val trues = Set("true", "1")
    val falses = Set("false", "0")

    flag {
      case None => Right(true)
      case Some(s) =>
        if (trues(s))
          Right(true)
        else if (falses(s))
          Right(false)
        else
          Left(s"Unrecognized flag value: $s")
    }
  }
  implicit def counter: ArgParser[Int @@ Counter] =
    flagAccumulator { (prevOpt, s) =>
      Right(Tag.of(prevOpt.fold(0)(Tag.unwrap) + 1))
    }

  // FIXME list and option below may not be fine with lists/options of flags
  implicit def list[T: ArgParser]: ArgParser[List[T]] =
    accumulator { (prevOpt, s) =>
      ArgParser[T].apply(None, s, mandatory = true).right.flatMap {
        case (false, _) =>
          // should not happen
          Left(s"Unrecognized value: $s")
        case (true, t) =>
          // inefficient for big lists
          Right(prevOpt.getOrElse(Nil) :+ t)
      }
    }
  implicit def option[T: ArgParser]: ArgParser[Option[T]] =
    accumulator { (prevOpt, s) =>
      ArgParser[T].apply(prevOpt.flatten, s, mandatory = true).right.flatMap {
        case (false, _) =>
          // should not happen
          Left(s"Unrecognized value: $s")
        case (true, t) =>
          Right(Some(t))
      }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy