summaryrefslogblamecommitdiff
path: root/core/src/main/scala/forge/define/Target.scala
blob: 98bd1b7cebc10ec77ad5b3cb85777ec571096663 (plain) (tree)
1
2
3
4
5
6
7
8
9
                    
 
                                          
                                         


                                        
 
                                         
                                            
 
                                                                 
     










                                                                              
             
      
                       

 

                                                          
                                              
                                           
                   
                    

                                  
                                                                                    
                                                                     







                                                                                      
 

                                                      
 

                                          
                                                                 
 
   
 
                                             
                           
   
                                                                       





                                    
                                                                     


                                         
                                                                                    

                                                 

   
                                                    
                                                              
                              

                                             

                    
 
                                              
                                                                                    

                                
                      

                                                           
                                                            

                                    
                                                   


                    

                                                                        
                                                                                    

                                                                           
   
























                                                                                                                                                                     
 
package forge.define

import ammonite.ops.{CommandResult, mkdir}
import forge.define.Applicative.Applyable
import forge.eval.PathRef
import forge.util.{Args, JsonFormatters}
import play.api.libs.json.{Format, Json}

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

abstract class Target[T] extends Target.Ops[T] with Applyable[T]{
  /**
    * What other Targets does this Target depend on?
    */
  val inputs: Seq[Target[_]]

  /**
    * Evaluate this target
    */
  def evaluate(args: Args): T

  /**
    * Even if this target's inputs did not change, does it need to re-evaluate
    * anyway?
    */
  def sideHash: Int = 0
}

object Target extends Applicative.Applyer[Target, Target]{
  def underlying[A](v: Target[A]) = v
  type Cacher = forge.define.Cacher[Target[_]]
  class Target0[T](t: T) extends Target[T]{
    lazy val t0 = t
    val inputs = Nil
    def evaluate(args: Args)  = t0
  }
  def apply[T](t: Target[T]): Target[T] = macro forge.define.Cacher.impl0[Target, T]
  def command[T](t: T): Target[T] = macro Applicative.impl[Target, T]
  def apply[T](t: T): Target[T] = macro impl[Target, T]
  def impl[M[_], T: c.WeakTypeTag](c: Context)
                                  (t: c.Expr[T])
                                  (implicit tt: c.WeakTypeTag[M[_]]): c.Expr[M[T]] = {
    forge.define.Cacher.wrapCached(c)(
      Applicative.impl(c)(t)
    )
  }

  abstract class Ops[T]{ this: Target[T] =>
    def map[V](f: T => V) = new Target.Mapped(this, f)

    def filter(f: T => Boolean) = this
    def withFilter(f: T => Boolean) = this
    def zip[V](other: Target[V]) = new Target.Zipped(this, other)

  }

  def traverse[T](source: Seq[Target[T]]) = {
    new Traverse[T](source)
  }
  class Traverse[T](val inputs: Seq[Target[T]]) extends Target[Seq[T]]{
    def evaluate(args: Args) = {
      for (i <- 0 until args.length)
      yield args(i).asInstanceOf[T]
    }

  }
  class Mapped[T, V](source: Target[T], f: T => V) extends Target[V]{
    def evaluate(args: Args) = f(args(0))
    val inputs = List(source)
  }
  class Zipped[T, V](source1: Target[T], source2: Target[V]) extends Target[(T, V)]{
    def evaluate(args: Args) = (args(0), args(1))
    val inputs = List(source1, source2)
  }

  def path(path: ammonite.ops.Path) = new Path(path)
  class Path(path: ammonite.ops.Path) extends Target[PathRef]{
    def handle = PathRef(path)
    def evaluate(args: Args) = handle
    override def sideHash = handle.hashCode()
    val inputs = Nil
  }

  class Subprocess(val inputs: Seq[Target[_]],
                   command: Args => Seq[String]) extends Target[Subprocess.Result] {

    def evaluate(args: Args) = {
      mkdir(args.dest)
      import ammonite.ops._
      implicit val path = ammonite.ops.Path(args.dest, pwd)
      val toTarget = () // Shadow the implicit conversion :/
      val output = %%(command(args))
      assert(output.exitCode == 0)
      Subprocess.Result(output, PathRef(args.dest))
    }
  }
  object Subprocess{
    case class Result(result: ammonite.ops.CommandResult, dest: PathRef)
    object Result{
      private implicit val crFormat: Format[CommandResult] = JsonFormatters.crFormat
      implicit val tsFormat: Format[Target.Subprocess.Result] = Json.format
    }
  }

  def map[A, B](t: Target[A], f: A => B) = t.map(f)
  def zip() =  new Target.Target0(())
  def zip[A](a: Target[A]) = a.map(Tuple1(_))
  def zip[A, B](a: Target[A], b: Target[B]) = a.zip(b)
  def zip[A, B, C](a: Target[A], b: Target[B], c: Target[C]) = new Target[(A, B, C)]{
    val inputs = Seq(a, b, c)
    def evaluate(args: Args) = (args[A](0), args[B](1), args[C](2))
  }
  def zip[A, B, C, D](a: Target[A], b: Target[B], c: Target[C], d: Target[D]) = new Target[(A, B, C, D)]{
    val inputs = Seq(a, b, c, d)
    def evaluate(args: Args) = (args[A](0), args[B](1), args[C](2), args[D](3))
  }
  def zip[A, B, C, D, E](a: Target[A], b: Target[B], c: Target[C], d: Target[D], e: Target[E]) = new Target[(A, B, C, D, E)]{
    val inputs = Seq(a, b, c, d, e)
    def evaluate(args: Args) = (args[A](0), args[B](1), args[C](2), args[D](3), args[E](4))
  }
  def zip[A, B, C, D, E, F](a: Target[A], b: Target[B], c: Target[C], d: Target[D], e: Target[E], f: Target[F]) = new Target[(A, B, C, D, E, F)]{
    val inputs = Seq(a, b, c, d, e, f)
    def evaluate(args: Args) = (args[A](0), args[B](1), args[C](2), args[D](3), args[E](4), args[F](5))
  }
  def zip[A, B, C, D, E, F, G](a: Target[A], b: Target[B], c: Target[C], d: Target[D], e: Target[E], f: Target[F], g: Target[G]) = new Target[(A, B, C, D, E, F, G)]{
    val inputs = Seq(a, b, c, d, e, f, g)
    def evaluate(args: Args) = (args[A](0), args[B](1), args[C](2), args[D](3), args[E](4), args[F](5), args[G](6))
  }
}