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

laika.directive.Builders.scala Maven / Gradle / Ivy

/*
 * Copyright 2013-2016 the original author or authors.
 *
 * 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 laika.directive

import laika.ast.~

/** Generic support for builders that allow to combine container types with
 *  matching type classes into a final result.
 * 
 *  The concrete use case for Laika is a concise and type-safe API for
 *  setting up directives or text roles. For these APIs and sample
 *  code see [[laika.directive.BuilderContext.dsl]], [[laika.rst.ext.Directives]] and [[laika.rst.ext.TextRoles]].
 * 
 *  This implementation is based on a concept outlined by Sadek Drobi in this gist: 
 *  [[https://gist.github.com/sadache/3646092]].
 *  The code used here is only a simplified subset of the demonstrated functionality.
 * 
 *  @author Sadek Drobi / Jens Halm
 */
object Builders {


  /** A wrapper for a single result value.
   */
  class Result[+A] (a: => A) {
    
    def get: A = a
    
    def map [B](f: A => B): Result[B] = new Result(f(get))
    
  }


  /** Contract for type classes that adapt a container
   *  type for use with these builders. Implementations
   *  have to know how to merge two containers as well
   *  as how to perform a simple map.
   */
  trait CanBuild [M[_]]{
    
    def apply [A,B](ma: M[A], mb: M[B]): M[A ~ B]
        
    def map [A,B](m: M[A], f: A => B): M[B]
            
  }

  /** Adds the `~` combinator function to all classes
    * that have a matching `CanBuild` type class.
    */
  trait Implicits {

    /** Allows to use the `~` combinator function on all classes
     *  that have a matching `CanBuild` type class.
     */
    implicit class BuilderOps [M[_],A](ma:M[A])(implicit fcb: CanBuild[M]) {

      def ~ [B](mb: M[B]): Builder[M]#CanBuild2[A,B] = {
        val b = new Builder(fcb)
        new b.CanBuild2(ma,mb)
      }

    }
  }

  /** Builders for using combinators for up to 12 result values.
   */
  class Builder [M[_]](canBuild: CanBuild[M]) {
   
    class CanBuild2 [A1,A2](m1: M[A1], m2: M[A2]) {
   
      def ~ [A3](m3: M[A3]) = new CanBuild3(canBuild(m1, m2), m3)
   
      def apply [B](f: (A1,A2) => B): M[B] =
        canBuild.map[A1 ~ A2, B](canBuild(m1, m2), { case a1 ~ a2 => f(a1, a2)})
    }
   
    class CanBuild3 [A1,A2,A3](m1: M[A1 ~ A2], m2: M[A3]) {
   
      def ~ [A4](m3: M[A4]) = new CanBuild4(canBuild(m1, m2), m3)
   
      def apply [B](f: (A1,A2,A3) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3, B](canBuild(m1, m2), { case a1 ~ a2 ~ a3 => f(a1, a2, a3) })
    }
   
    class CanBuild4 [A1,A2,A3,A4](m1: M[A1 ~ A2 ~ A3], m2: M[A4]) {
   
      def ~ [A5](m3: M[A5]) = new CanBuild5(canBuild(m1,m2), m3)
   
      def apply [B](f: (A1,A2,A3,A4) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4, B](canBuild(m1, m2), { case a1 ~ a2 ~ a3 ~ a4 => f(a1, a2, a3, a4) })
    }
   
    class CanBuild5 [A1,A2,A3,A4,A5](m1: M[A1 ~ A2 ~ A3 ~ A4], m2: M[A5]) {
      
      def ~ [A6](m3: M[A6]) = new CanBuild6(canBuild(m1,m2), m3)
   
      def apply [B](f: (A1,A2,A3,A4,A5) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4 ~ A5, B](canBuild(m1, m2), { 
          case a1 ~ a2 ~ a3 ~ a4 ~ a5 => f(a1, a2, a3, a4, a5) 
      })
    }
    
    class CanBuild6 [A1,A2,A3,A4,A5,A6](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5], m2: M[A6]) {
      
      def ~ [A7](m3: M[A7]) = new CanBuild7(canBuild(m1,m2), m3)
   
      def apply [B](f: (A1,A2,A3,A4,A5,A6) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6, B](canBuild(m1, m2), { 
          case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 => f(a1, a2, a3, a4, a5, a6) 
      })
    }
    
    class CanBuild7 [A1,A2,A3,A4,A5,A6,A7](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6], m2: M[A7]) {
      
      def ~ [A8](m3: M[A8]) = new CanBuild8(canBuild(m1,m2), m3)
   
      def apply [B](f: (A1,A2,A3,A4,A5,A6,A7) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7, B](canBuild(m1, m2), { 
          case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 ~ a7 => f(a1, a2, a3, a4, a5, a6, a7) 
      })
    }
    
    class CanBuild8 [A1,A2,A3,A4,A5,A6,A7,A8](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7], m2: M[A8]) {
      
      def ~ [A9](m3: M[A9]) = new CanBuild9(canBuild(m1,m2), m3)
   
      def apply [B](f: (A1,A2,A3,A4,A5,A6,A7,A8) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8, B](canBuild(m1, m2), { 
          case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 ~ a7 ~ a8 => f(a1, a2, a3, a4, a5, a6, a7, a8) 
      })
    }
    
    class CanBuild9 [A1,A2,A3,A4,A5,A6,A7,A8,A9](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8], m2: M[A9]) {
      
      def ~ [A10](m3: M[A10]) = new CanBuild10(canBuild(m1,m2), m3)
   
      def apply [B](f: (A1,A2,A3,A4,A5,A6,A7,A8,A9) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9, B](canBuild(m1, m2), { 
          case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 ~ a7 ~ a8 ~ a9 => f(a1, a2, a3, a4, a5, a6, a7, a8, a9) 
      })
    }
    
    class CanBuild10 [A1,A2,A3,A4,A5,A6,A7,A8,A9,A10](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9], m2: M[A10]) {
      
      def ~ [A11](m3: M[A11]) = new CanBuild11(canBuild(m1,m2), m3)
   
      def apply [B](f: (A1,A2,A3,A4,A5,A6,A7,A8,A9,A10) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10, B](canBuild(m1, m2), { 
          case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 ~ a7 ~ a8 ~ a9 ~ a10 => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) 
      })
    }
    
    class CanBuild11 [A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10], m2: M[A11]) {
      
      def ~ [A12](m3: M[A12]) = new CanBuild12(canBuild(m1,m2), m3)
   
      def apply [B](f: (A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10 ~ A11, B](canBuild(m1, m2), { 
          case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 ~ a7 ~ a8 ~ a9 ~ a10 ~ a11 => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) 
      })
    }
    
    class CanBuild12 [A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10 ~ A11], m2: M[A12]) {
      
      def apply [B](f: (A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12) => B): M[B] =
        canBuild.map[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10 ~ A11 ~ A12, B](canBuild(m1, m2), { 
          case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 ~ a7 ~ a8 ~ a9 ~ a10 ~ a11 ~ a12 => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) 
      })
    }

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy