2009-08-17 16:51:43 +02:00
/* sbt -- Simple Build Tool
* Copyright 2008 , 2009 Mark Harrah
*/
2010-06-09 06:56:07 +02:00
package sbt
2009-08-17 16:51:43 +02:00
2010-11-24 20:09:02 +01:00
import java.io.File
2009-08-17 16:51:43 +02:00
2010-06-14 04:59:29 +02:00
trait Mapper
2009-08-17 16:51:43 +02:00
{
2010-06-14 04:59:29 +02:00
type PathMap = File => Option [ String ]
type FileMap = File => Option [ File ]
2009-08-26 14:38:20 +02:00
2012-01-27 03:28:19 +01:00
/* * A path mapper that pairs a File with the path returned by calling `getPath` on it. */
2010-06-14 04:59:29 +02:00
val basic : PathMap = f => Some ( f . getPath )
2012-01-27 03:28:19 +01:00
/* * A path mapper that pairs a File with its path relative to `base`.
* If the File is not a descendant of `base` , it is not handled ( None is returned by the mapper ) . */
2010-06-14 04:59:29 +02:00
def relativeTo ( base : File ) : PathMap = IO . relativize ( base , _ )
2012-01-27 03:28:19 +01:00
2011-02-10 14:14:51 +01:00
def relativeTo ( bases : Iterable [ File ] , zero : PathMap = transparent ) : PathMap = fold ( zero , bases ) ( relativeTo )
2012-01-27 03:28:19 +01:00
/* * A path mapper that pairs a descendent of `oldBase` with `newBase` prepended to the path relative to `oldBase`.
* For example , if `oldBase = /old/x/` and `newBase = new/a/` , then `/old/x/y/z.txt` gets paired with `new/a/y/z.txt` . */
def rebase ( oldBase : File , newBase : String ) : PathMap =
2010-06-14 04:59:29 +02:00
{
2012-01-27 03:28:19 +01:00
val normNewBase = normalizeBase ( newBase )
2010-06-14 04:59:29 +02:00
( file : File ) =>
if ( file == oldBase )
2012-01-27 03:28:19 +01:00
Some ( if ( normNewBase . isEmpty ) "." else normNewBase )
2010-06-14 04:59:29 +02:00
else
2012-01-27 03:28:19 +01:00
IO . relativize ( oldBase , file ) . map ( normNewBase + _ )
2010-06-14 04:59:29 +02:00
}
2012-01-27 03:28:19 +01:00
/* * A mapper that throws an exception for any input. This is useful as the last mapper in a pipeline to ensure every input gets mapped. */
2010-06-14 04:59:29 +02:00
def fail : Any => Nothing = f => error ( "No mapping for " + f )
2012-01-27 03:28:19 +01:00
/* * A path mapper that pairs a File with its name. For example, ` / x / y / z.txt` gets paired with `z.txt`. */
2010-06-14 04:59:29 +02:00
val flat : PathMap = f => Some ( f . getName )
2012-01-27 03:28:19 +01:00
/* * A path mapper that pairs a File with a path constructed from `newBase` and the file's name.
* For example , if `newBase = /new/a/` , then `/old/x/z.txt` gets paired with `/new/a/z.txt` . */
def flatRebase ( newBase : String ) : PathMap =
2009-12-05 16:31:06 +01:00
{
2012-01-27 03:28:19 +01:00
val newBase0 = normalizeBase ( newBase )
f => Some ( newBase0 + f . getName )
2009-12-05 16:31:06 +01:00
}
2012-01-27 03:28:19 +01:00
/* * A mapper that is defined on all inputs by the function `f`. */
2011-02-10 14:14:51 +01:00
def total [ A ,B ] ( f : A => B ) : A => Some [ B ] = x => Some ( f ( x ) )
2012-01-27 03:28:19 +01:00
/* * A mapper that ignores all inputs. */
2011-02-10 14:14:51 +01:00
def transparent : Any => Option [ Nothing ] = _ => None
2010-06-14 04:59:29 +02:00
def normalizeBase ( base : String ) = if ( ! base . isEmpty && ! base . endsWith ( "/" ) ) base + "/" else base
2012-01-27 03:28:19 +01:00
/* * Pairs a File with the absolute File obtained by calling `getAbsoluteFile`.
* Note that this usually means that relative files are resolved against the current working directory . */
2010-06-14 04:59:29 +02:00
def abs : FileMap = f => Some ( f . getAbsoluteFile )
2012-01-27 03:28:19 +01:00
/* * Returns a File mapper that resolves a relative File against `newDirectory` and pairs the original File with the resolved File.
* The mapper ignores absolute files . */
def resolve ( newDirectory : File ) : FileMap = file => if ( file . isAbsolute ) None else Some ( new File ( newDirectory , file . getPath ) )
2011-02-10 14:14:51 +01:00
def rebase ( oldBases : Iterable [ File ] , newBase : File , zero : FileMap = transparent ) : FileMap =
2011-05-17 04:56:07 +02:00
fold ( zero , oldBases ) ( old => rebase ( old , newBase ) )
2012-01-27 03:28:19 +01:00
/* * Produces a File mapper that pairs a descendant of `oldBase` with a file in `newBase` that preserving the relative path of the original file against `oldBase`.
* For example , if `oldBase` is `/old/x/` and `newBase` is `/new/a/` , `/old/x/y/z.txt` gets paired with `/new/a/y/z.txt` .
* */
2010-06-14 04:59:29 +02:00
def rebase ( oldBase : File , newBase : File ) : FileMap =
file =>
if ( file == oldBase )
Some ( newBase )
else
IO . relativize ( oldBase , file ) map { r => new File ( newBase , r ) }
2012-01-27 03:28:19 +01:00
/* * Constructs a File mapper that pairs a file with a file with the same name in `newDirectory`.
* For example , if `newDirectory` is `/a/b` , then `/r/s/t/d.txt` will be paired with `/a/b/d.txt` */
2010-06-14 04:59:29 +02:00
def flat ( newDirectory : File ) : FileMap = file => Some ( new File ( newDirectory , file . getName ) )
2011-02-10 14:14:51 +01:00
import Alternatives._
2012-01-27 03:28:19 +01:00
/* * Selects all descendents of `base` directory and maps them to a path relative to `base`.
* `base` itself is not included . */
def allSubpaths ( base : File ) : Traversable [ ( File ,String ) ] =
selectSubpaths ( base , AllPassFilter )
/* * Selects descendents of `base` directory matching `filter` and maps them to a path relative to `base`.
* `base` itself is not included . */
def selectSubpaths ( base : File , filter : FileFilter ) : Traversable [ ( File ,String ) ] =
( PathFinder ( base ) ** filter --- PathFinder ( base ) ) pair ( relativeTo ( base ) | flat )
2011-02-10 14:14:51 +01:00
private [ this ] def fold [ A ,B ,T ] ( zero : A => Option [ B ] , in : Iterable [ T ] ) ( f : T => A => Option [ B ] ) : A => Option [ B ] =
( zero /: in ) ( ( mapper , base ) => f ( base ) | mapper )
2010-06-14 04:59:29 +02:00
}
trait Alternative [ A ,B ] { def | ( g : A => Option [ B ] ) : A => Option [ B ] }
trait Alternatives
{
implicit def alternative [ A ,B ] ( f : A => Option [ B ] ) : Alternative [ A ,B ] =
new Alternative [ A ,B ] { def | ( g : A => Option [ B ] ) =
( a : A ) => f ( a ) orElse g ( a )
}
2010-11-24 20:09:02 +01:00
final def alternatives [ A ,B ] ( alts : Seq [ A => Option [ B ] ] ) : A => Option [ B ] =
alts match
{
case Seq ( f , fs @ _ * ) => f | alternatives ( fs )
case Seq ( ) => a => None
}
2011-02-10 14:14:51 +01:00
}
object Alternatives extends Alternatives