Skip to content

Instantly share code, notes, and snippets.

@erikkaplun
Last active August 29, 2015 14:08
Show Gist options
  • Save erikkaplun/eea6b21f8e5154e0c97e to your computer and use it in GitHub Desktop.
Save erikkaplun/eea6b21f8e5154e0c97e to your computer and use it in GitHub Desktop.
/**
* Copyright (c) 2014 Erik Allik
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
import scalaz._, Scalaz._
object InputValidation {
type Va[A] = Validation[String, A]
type VaNel[A] = ValidationNel[String, A]
type Vali[A, B] = Kleisli[Va, A, B]
/** Wraps a validator in Kleisli so that it could be piped from/into another Kleisli wrapped validator */
def Vali[In, Out](fn: In => Va[Out]): Vali[In, Out] = Kleisli[Va, In, Out](fn)
// for Kleisli and its >=>
implicit val vaBinding = new Bind[Va] {
def map[A, B](fa: Va[A])(f: A => B): Va[B] = fa.map(f)
def bind[A, B](fa: Va[A])(f: A => Va[B]): Va[B] = {
import scalaz.Validation.FlatMap._
fa.flatMap(f)
}
}
import shapeless._, contrib.scalaz._, syntax.std.tuple._
import syntax.std.function._, ops.function._
import shapeless.ops.hlist._
private object toValidationNel extends Poly1 {
implicit def apply[T] = at[Va[T]](_.toValidationNel)
}
/**
* Usage:
*
* val postal = "12345".some
* val country = "US".some
*
* val params = (
* postal |> nonEmpty("postal is required"),
* country |> nonEmpty("country is required") >=> validCountry("country must be a valid 2 letter ISO country code")
* )
*
* validate(params) { (postal: String, country: String) =>
* println(s"postal = $postal, country = $country")
* }
*/
def validate[P <: Product, F, L1 <: HList, L2 <: HList, L3 <: HList, R](params: P)(block: F)(
implicit
gen: Generic.Aux[P, L1],
mp: Mapper.Aux[toValidationNel.type, L1, L2],
seq: Sequencer.Aux[L2, VaNel[L3]],
fn: FnToProduct.Aux[F, L3 => R]
): VaNel[R] = {
sequence(gen.to(params).map(toValidationNel)).map(block.toProduct)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment