2009-08-17 10:51:43 -04:00
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
2010-06-09 00:56:07 -04:00
package sbt
2009-08-17 10:51:43 -04:00
2010-11-24 14:09:02 -05:00
import java.io.File
2009-08-17 10:51:43 -04:00
2010-06-13 22:59:29 -04:00
trait Mapper
2009-08-17 10:51:43 -04:00
{
2010-06-13 22:59:29 -04:00
type PathMap = File => Option [ String ]
type FileMap = File => Option [ File ]
2009-08-26 08:38:20 -04:00
2012-01-26 21:28:19 -05:00
/** A path mapper that pairs a File with the path returned by calling `getPath` on it.*/
2010-06-13 22:59:29 -04:00
val basic : PathMap = f => Some ( f . getPath )
2012-01-26 21:28:19 -05: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-13 22:59:29 -04:00
def relativeTo ( base : File ) : PathMap = IO . relativize ( base , _ )
2012-01-26 21:28:19 -05:00
2011-02-10 08:14:51 -05:00
def relativeTo ( bases : Iterable [ File ], zero : PathMap = transparent ) : PathMap = fold ( zero , bases )( relativeTo )
2012-01-26 21:28:19 -05: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-13 22:59:29 -04:00
{
2012-01-26 21:28:19 -05:00
val normNewBase = normalizeBase ( newBase )
2010-06-13 22:59:29 -04:00
( file : File ) =>
if ( file == oldBase )
2012-01-26 21:28:19 -05:00
Some ( if ( normNewBase . isEmpty ) "." else normNewBase )
2010-06-13 22:59:29 -04:00
else
2012-01-26 21:28:19 -05:00
IO . relativize ( oldBase , file ). map ( normNewBase + _ )
2010-06-13 22:59:29 -04:00
}
2012-01-26 21:28:19 -05: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-13 22:59:29 -04:00
def fail : Any => Nothing = f => error ( "No mapping for " + f )
2012-01-26 21:28:19 -05:00
/** A path mapper that pairs a File with its name. For example, `/x/y/z.txt` gets paired with `z.txt`.*/
2010-06-13 22:59:29 -04:00
val flat : PathMap = f => Some ( f . getName )
2012-01-26 21:28:19 -05: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 10:31:06 -05:00
{
2012-01-26 21:28:19 -05:00
val newBase0 = normalizeBase ( newBase )
f => Some ( newBase0 + f . getName )
2009-12-05 10:31:06 -05:00
}
2012-01-26 21:28:19 -05:00
/** A mapper that is defined on all inputs by the function `f`.*/
2011-02-10 08:14:51 -05:00
def total [ A ,B ]( f : A => B ) : A => Some [ B ] = x => Some ( f ( x ))
2012-01-26 21:28:19 -05:00
/** A mapper that ignores all inputs.*/
2011-02-10 08:14:51 -05:00
def transparent : Any => Option [ Nothing ] = _ => None
2010-06-13 22:59:29 -04:00
def normalizeBase ( base : String ) = if (! base . isEmpty && ! base . endsWith ( "/" )) base + "/" else base
2012-01-26 21:28:19 -05: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-13 22:59:29 -04:00
def abs : FileMap = f => Some ( f . getAbsoluteFile )
2012-01-26 21:28:19 -05: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 08:14:51 -05:00
def rebase ( oldBases : Iterable [ File ], newBase : File , zero : FileMap = transparent ) : FileMap =
2011-05-16 22:56:07 -04:00
fold ( zero , oldBases )( old => rebase ( old , newBase ))
2012-01-26 21:28:19 -05: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-13 22:59:29 -04: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-26 21:28:19 -05: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-13 22:59:29 -04:00
def flat ( newDirectory : File ) : FileMap = file => Some ( new File ( newDirectory , file . getName ))
2011-02-10 08:14:51 -05:00
import Alternatives._
2012-01-26 21:28:19 -05: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 08:14:51 -05: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-13 22:59:29 -04: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 14:09:02 -05: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 08:14:51 -05:00
}
object Alternatives extends Alternatives