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

com.paypal.cascade.common.option.option.scala Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2013-2014 PayPal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.paypal.cascade.common

import scala.concurrent.Future

/**
 * Convenience methods and implicit wrappers for working with [[scala.Option]]
 */

package object option {

  /**
   * A convenience method borrowed from Scalaz. Produces an explicitly typed None.
   * @tparam T the Option type
   * @return a None of type Option[T]
   */
  def none[T]: Option[T] = None: Option[T]

  /**
   * Allows any object to be wrapped in a Some or an Option. Note that some objects provide their own
   * `toOption` or similar methods; provided methods should be preferred over these implicits, in these cases.
   *
   * {{{
   *   import com.paypal.cascade.common.option._
   *   val a = 5.some // Option[Int], equal to Some(5)
   * }}}
   *
   * @param any the wrapped object
   * @tparam T the wrapped object's type, and the type of the resulting Option
   */
  implicit class Any2Option[T](any: T) {

    /**
     * Wraps the given object in a Some.
     *
     * NOTE: calling `.some` on a null value will return `Some(null): Some[T]`, which is legal but broken.
     * This behavior is equivalent to directly crafting a `Some[T](null)`, which is also legal but broken.
     * Overall, prefer `.opt` for null safety.
     * @return the wrapped object
     */
    def some: Option[T] = Some(any)

    /**
     * Wraps the given object in an Option, which resolves to None if the given object reference is null.
     * @return optionally, the wrapped object
     */
    def opt: Option[T] = Option(any)
  }

  /**
   * Extends an Option for side effects based on the optional value.
   * @param option the wrapped Option
   * @tparam T the type of the optional value
   */
  implicit class RichOption[T](option: Option[T]) {

    /**
     * If this Option is None, perform the action in fn
     * @param fn the action to perform
     * @return the wrapped Option (identity)
     */
    def executeIfNone(fn: => Unit): Option[T] = {
      sideEffectNone(fn)
    }

    /**
     * If this Option is none, perform the action in fn
     * @param fn the action to perform
     * @return the wrapped Option (identity)
     */
    def sideEffectNone(fn: => Unit): Option[T] = {
      if(option.isEmpty) {
        fn
      }
      option
    }

    /**
     * If this Option is Some, perform the action in fn which acts on the value
     * @param fn the action to perform
     * @return the wrapped Option (identity)
     */
    def sideEffectSome(fn: T => Unit): Option[T] = {
      option.foreach(fn)
      option
    }

    /**
     * If this Option is None, throw the given Throwable; else, return the value inside the Option
     * @param t the Throwable to potentially throw
     * @return the value inside the wrapped Option
     * @throws Throwable if the Option is None
     */
    @throws[Throwable]
    def orThrow(t: => Throwable): T = option.getOrElse(throw t)

    /**
     * Map the Option into Future success/fail states: if the Option is a Some, yield a Future with its value;
     * if the Option is None, yield a Future with Throwable t as its failure state
     * @param t the Throwable to potentially wrap as a failure
     * @return the Future-wrapped value of this Option, or a Future-wrapped failure Throwable
     */
    def toFuture(t: => Throwable): Future[T] = {
      option.map(Future.successful).getOrElse(Future.failed(t))
    }
  }

  /**
   * Convenience methods for working with pairs of Options.
   *
   * {{{
   *   import com.paypal.cascade.common.option._
   *   val a = (Some("hi"), None)
   *   a.fold( ... )
   * }}}
   */
  implicit class RichOptionTuple[T, U](optionTuple: (Option[T], Option[U])) {
    /**
     * Implements fold across a pair of Options
     * @param bothSome if both Options are defined
     * @param leftSome if only the first Option is defined
     * @param rightSome if only the second Option is defined
     * @param bothNone if neither is defined
     * @tparam V the resulting type of this fold
     * @return the result of a pattern match over a pair
     */
    def fold[V](bothSome: (T, U) => V,
                leftSome: T => V,
                rightSome: U => V,
                bothNone: => V): V = optionTuple match {
      case (Some(t), Some(u)) => bothSome(t, u)
      case (Some(t), None) => leftSome(t)
      case (None, Some(u)) => rightSome(u)
      case (None, None) => bothNone
    }
  }

  /**
   * Convenience methods for working with Option[Boolean] types
   * @param optionBoolean this Option
   *
   * {{{
   *   import com.paypal.cascade.common.option._
   *   val a = Some(true)
   *   a.orFalse  // => true, from the value in a
   *   a.orTrue   // => true still, from the value in a
   *
   *   val b = None
   *   b.orFalse  // => false, because b is not defined
   *   b.orTrue   // => true, because b is not defined
   * }}}
   */
  implicit class RichOptionBoolean(optionBoolean: Option[Boolean]) {

    /**
     * Either the value inside this option, or false
     * @return the value inside this option, or false
     */
    def orFalse: Boolean = optionBoolean.getOrElse(false)

    /**
     * Either the value inside this option, or true
     * @return the value inside this option, or true
     */
    def orTrue: Boolean = optionBoolean.getOrElse(true)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy