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

anorm.RowParser.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 
 */

package anorm

trait RowParser[+A] extends (Row => SqlResult[A]) { parent =>

  /**
   * Returns a parser that will apply given function `f`
   * to the result of this first parser. If the current parser is not
   * successful, the new one will return encountered [[Error]].
   *
   * @param f Function applied on the successful parser result
   *
   * {{{
   * import anorm.{ RowParser, SqlParser }
   *
   * val parser: RowParser[Int] = SqlParser.str("col").map(_.length)
   * // Prepares a parser that first get 'col' string value,
   * // and then returns the length of that
   * }}}
   */
  def map[B](f: A => B): RowParser[B] = RowParser(parent.andThen(_.map(f)))

  /**
   * Returns parser which collects information
   * from already parsed row data using `f`.
   *
   * @param otherwise Message returned as error if nothing can be collected using `f`.
   * @param f Collecting function
   */
  def collect[B](otherwise: String)(f: PartialFunction[A, B]): RowParser[B] =
    RowParser(parent(_).flatMap(f.lift(_).fold[SqlResult[B]](Error(SqlMappingError(otherwise)))(Success(_))))

  def flatMap[B](k: A => RowParser[B]): RowParser[B] =
    RowParser(row => parent(row).flatMap(k(_)(row)))

  /**
   * Combines this parser on the left of the parser `p` given as argument.
   *
   * @param p Parser on the right
   *
   * {{{
   * import anorm._, SqlParser.{ int, str }
   *
   * def populations(implicit con: java.sql.Connection): List[String ~ Int] =
   *   SQL("SELECT * FROM Country").as((str("name") ~ int("population")).*)
   * }}}
   */
  def ~[B](p: RowParser[B]): RowParser[A ~ B] =
    RowParser(row => parent(row).flatMap(a => p(row).map(new ~(a, _))))

  /**
   * Combines this current parser with the one given as argument `p`,
   * if and only if the current parser can first/on left side successfully
   * parse a row, without keeping these values in parsed result.
   *
   * {{{
   * import anorm._, SqlParser.{ int, str }
   *
   * def string(implicit con: java.sql.Connection) = SQL("SELECT * FROM test").
   *   as((int("id") ~> str("val")).single)
   * // row has to have an int column 'id' and a string 'val' one,
   * // keeping only 'val' in result
   * }}}
   */
  def ~>[B](p: RowParser[B]): RowParser[B] =
    RowParser(row => parent(row).flatMap(_ => p(row)))

  /**
   * Combines this current parser with the one given as argument `p`,
   * if and only if the current parser can first successfully
   * parse a row, without keeping the values of the parser `p`.
   *
   * {{{
   * import anorm._, SqlParser.{ int, str }
   *
   * def i(implicit con: java.sql.Connection) = SQL("SELECT * FROM test").
   *   as((int("id") <~ str("val")).single)
   * // row has to have an int column 'id' and a string 'val' one,
   * // keeping only 'id' in result
   * }}}
   */
  def <~[B](p: RowParser[B]): RowParser[A] = parent.~(p).map(_._1)

  // TODO: Scaladoc
  def |[B >: A](p: RowParser[B]): RowParser[B] = RowParser { row =>
    parent(row) match {
      case Error(_) => p(row)
      case a        => a
    }
  }

  /**
   * Returns a row parser for optional column,
   * that will turn missing or null column as None.
   */
  def ? : RowParser[Option[A]] = RowParser {
    parent(_) match {
      case Success(a) => Success(Some(a))
      case Error(ColumnNotFound(_, _)) =>
        Success(None)

      case e @ Error(_) => e
    }
  }

  /** Alias for [[flatMap]] */
  def >>[B](f: A => RowParser[B]): RowParser[B] = flatMap(f)

  /**
   * Returns possibly empty list parsed from result.
   *
   * {{{
   * import anorm._, SqlParser.scalar
   *
   * val price = 125
   *
   * def foo(implicit con: java.sql.Connection) =
   *   SQL"SELECT name FROM item WHERE price < \\$price".as(scalar[String].*)
   * }}}
   */
  def * : ResultSetParser[List[A]] = ResultSetParser.list(parent)

  /**
   * Returns non empty list parse from result,
   * or raise error if there is no result.
   *
   * {{{
   * import anorm._, SqlParser.str
   *
   * def foo(implicit con: java.sql.Connection) = {
   *   val parser = str("title") ~ str("descr")
   *   SQL("SELECT title, descr FROM pages").as(parser.+) // at least 1 page
   * }
   * }}}
   */
  def + : ResultSetParser[List[A]] = ResultSetParser.nonEmptyList(parent)

  /**
   * Returns a result set parser expecting exactly one row to parse.
   *
   * {{{
   * import anorm._, SqlParser.scalar
   *
   * def b(implicit con: java.sql.Connection): Boolean =
   *   SQL("SELECT flag FROM Test WHERE id = :id").
   *     on("id" -> 1).as(scalar[Boolean].single)
   * }}}
   *
   * @see #singleOpt
   */
  def single = ResultSetParser.single(parent)

  /**
   * Returns a result set parser for none or one parsed row.
   *
   * {{{
   * import anorm._, SqlParser.scalar
   *
   * def name(implicit con: java.sql.Connection): Option[String] =
   *   SQL("SELECT name FROM Country WHERE lang = :lang")
   *     .on("lang" -> "notFound").as(scalar[String].singleOpt)
   * }}}
   */
  def singleOpt: ResultSetParser[Option[A]] = ResultSetParser.singleOpt(parent)

}

object RowParser {
  def apply[A](f: Row => SqlResult[A]): RowParser[A] = new RowParser[A] {
    def apply(row: Row): SqlResult[A] = f(row)
  }

  /** Row parser that result in successfully unchanged row. */
  object successful extends RowParser[Row] {
    def apply(row: Row): SqlResult[Row] = Success(row)
  }

  def failed[A](error: => Error): RowParser[A] = new RowParser[A] {
    def apply(row: Row): SqlResult[A] = error
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy