Counts all lines in source code files.
module lines;
import std.file;
import std.path;
import std.stdio;
import std.regex;
import std.getopt;
import std.string;
import std.traits;
import std.algorithm;
import io = std.stream;
/+
Usage: lines [flags...]
Flags:
-d, --directory [string] The directory where the source file are located (scanned recursively)
-f, --filter [string] A GLOB pattern to filter out any irrelevant files.
-e, -[no]exclude [verb] Whether or not to exclude empty lines from the final count.
+/
int main( string[] args )
{
string sourceDir = null;
string filters = "*";
bool excludeEmptyLines = true;
args.getopt(
"d|directory", &sourceDir,
"e|[no]empty", &excludeEmptyLines,
"f|filter", &filters,
);
if( sourceDir is null )
{
stderr.writeln( "Cannot find the path specified." );
return 1;
}
sourceDir = sourceDir.absolutePath;
if( !sourceDir.exists || !sourceDir.isDir )
{
stderr.writeln( "Cannot find the path specified." );
return 1;
}
ulong files;
ulong[] lines;
try
{
auto entries = sourceDir.dirEntries( filters, SpanMode.breadth, true );
foreach( e; entries )
{
if( !e.isFile )
continue;
auto f = new io.File( e.name, io.FileMode.In );
foreach( char[] l; f )
{
if( excludeEmptyLines && l.strip().length == 0 )
continue;
lines ~= l.length;
}
++files;
}
}
catch( Throwable th )
{
stderr.writefln( "Could not retrieve files. Reason: %s", th.msg );
return 2;
}
ulong totalLines = lines.reduce!( ( x, y ) => x + y );
"Found %s lines in %s files".writefln( lines.length.comify(), files.comify() );
"Average line length: %s".writefln( lines.mean().comify() );
return 0;
}
ulong mean( T )( T[] numbers ) if( isNumeric!T )
{
auto sum = numbers.reduce!( ( x, y ) => x + y );
return sum / numbers.length;
}
string comify( T )( T number ) if( isNumeric!T )
{
auto rex = r"(?<=\d)(?=(\d\d\d)+\b)".regex( "g" );
return "%s".format( number ).replaceAll( rex, "," );
}