D-Scanner/src/dscanner/utils.d

231 lines
5.4 KiB
D

module dscanner.utils;
import std.array : appender, uninitializedArray;
import std.stdio : stdin, stderr, File;
import std.conv : to;
import std.encoding : BOM, BOMSeq, EncodingException, getBOM;
import std.format : format;
import std.file : exists, read;
private void processBOM(ubyte[] sourceCode, string fname)
{
enum spec = "D-Scanner does not support %s-encoded files (%s)";
const BOMSeq bs = sourceCode.getBOM;
with(BOM) switch (bs.schema)
{
case none, utf8:
break;
default:
throw new EncodingException(spec.format(bs.schema, fname));
}
sourceCode = sourceCode[bs.sequence.length .. $];
}
unittest
{
import std.exception : assertThrown, assertNotThrown;
import std.encoding : bomTable;
import std.traits : EnumMembers;
foreach(m ; EnumMembers!BOM)
{
auto sc = bomTable[m].sequence.dup;
if (m != BOM.none && m != BOM.utf8)
{
assertThrown!(EncodingException)(processBOM(sc, ""));
}
else
{
assertNotThrown!(EncodingException)(processBOM(sc, ""));
}
}
}
ubyte[] readStdin()
{
auto sourceCode = appender!(ubyte[])();
ubyte[4096] buf;
while (true)
{
auto b = stdin.rawRead(buf);
if (b.length == 0)
break;
sourceCode.put(b);
}
sourceCode.data.processBOM("stdin");
return sourceCode.data;
}
ubyte[] readFile(string fileName)
{
if (fileName == "stdin")
return readStdin();
if (!exists(fileName))
{
stderr.writefln("%s does not exist", fileName);
return [];
}
File f = File(fileName);
ubyte[] sourceCode;
sourceCode = cast(ubyte[]) fileName.read();
sourceCode.processBOM(fileName);
return sourceCode;
}
string[] expandArgs(string[] args)
{
import std.file : isFile, FileException, dirEntries, SpanMode;
import std.algorithm.iteration : map;
import std.algorithm.searching : endsWith;
// isFile can throw if it's a broken symlink.
bool isFileSafe(T)(T a)
{
try
return isFile(a);
catch (FileException)
return false;
}
string[] rVal;
if (args.length == 1)
args ~= ".";
foreach (arg; args[1 .. $])
{
if (arg == "stdin" || isFileSafe(arg))
rVal ~= arg;
else
foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name))
{
if (isFileSafe(item) && (item.endsWith(`.d`) || item.endsWith(`.di`)))
rVal ~= item;
else
continue;
}
}
return rVal;
}
/**
* Allows to build access chains of class members as done with the $(D ?.) operator
* in other languages. In the chain, any $(D null) member that is a class instance
* or that returns one, has for effect to shortcut the complete evaluation.
*
* This function is copied from https://github.com/BBasile/iz to avoid a new submodule.
* Any change made to this copy should also be applied to the origin.
*
* Params:
* M = The class type of the chain entry point.
*
* Bugs:
* Assigning a member only works with $(D unwrap).
*
*/
struct SafeAccess(M)
if (is(M == class))
{
M m;
@disable this();
/**
* Instantiate.
*
* Params:
* m = An instance of the entry point type. It is usually only
* $(D null) when the constructor is used internally, to build
* the chain.
*/
this(M m)
{
this.m = m;
}
alias m this;
/// Unprotect the class instance.
alias unwrap = m;
/// Handles safe access.
auto ref opDispatch(string member, A...)(auto ref A a)
{
import std.traits : ReturnType;
alias T = typeof(__traits(getMember, m, member));
static if (is(T == class))
{
return (!m || !__traits(getMember, m, member))
? SafeAccess!T(null)
: SafeAccess!T(__traits(getMember, m, member));
}
else
{
import std.traits : ReturnType, Parameters, isFunction;
static if (isFunction!T)
{
// otherwise there's a missing return statement.
alias R = ReturnType!T;
static if (!is(R == void) &&
!(is(R == class) && Parameters!T.length == 0))
pragma(msg, __FILE__ ~ "(" ~ __LINE__.stringof ~ "): error, " ~
"only `void function`s or `class` getters can be called without unwrap");
static if (is(R == class))
{
return (m is null)
? SafeAccess!R(null)
: SafeAccess!R(__traits(getMember, m, member)(a));
}
else
{
if (m)
__traits(getMember, m, member)(a);
}
}
else
{
if (m)
__traits(getMember, m, member) = a;
}
}
}
}
/// General usage
@safe unittest
{
class LongLineOfIdent3{int foo; void setFoo(int v) @safe{foo = v;}}
class LongLineOfIdent2{LongLineOfIdent3 longLineOfIdent3;}
class LongLineOfIdent1{LongLineOfIdent2 longLineOfIdent2;}
class Root {LongLineOfIdent1 longLineOfIdent1;}
SafeAccess!Root sar = SafeAccess!Root(new Root);
// without the SafeAccess we would receive a SIGSEGV here
sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.setFoo(0xDEADBEEF);
bool notAccessed = true;
// the same with `&&` whould be much longer
if (LongLineOfIdent3 a = sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3)
{
notAccessed = false;
}
assert(notAccessed);
// checks that forwarding actually works
sar.m.longLineOfIdent1 = new LongLineOfIdent1;
sar.m.longLineOfIdent1.longLineOfIdent2 = new LongLineOfIdent2;
sar.m.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3 = new LongLineOfIdent3;
sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.setFoo(42);
assert(sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.unwrap.foo == 42);
}
/**
* IFTI helper for $(D SafeAccess).
*
* Returns:
* $(D m) with the ability to safely access its members that are class
* instances.
*/
auto ref safeAccess(M)(M m)
{
return SafeAccess!M(m);
}