/**
   Exception classes
 */
module unit_threaded.exception;

void fail(const string output, const string file, size_t line) @safe pure
{
    throw new UnitTestException([output], file, line);
}

void fail(const string[] lines, const string file, size_t line) @safe pure
{
    throw new UnitTestException(lines, file, line);
}

/**
 * An exception to signal that a test case has failed.
 */
class UnitTestException : Exception
{
    this(const string msg, string file = __FILE__,
         size_t line = __LINE__, Throwable next = null) @safe pure nothrow
    {
        this([msg], file, line, next);
    }

    this(const string[] msgLines, string file = __FILE__,
         size_t line = __LINE__, Throwable next = null) @safe pure nothrow
    {
        import std.string: join;
        super(msgLines.join("\n"), next, file.dup, line);
        this.msgLines = msgLines.dup;
    }

    override string toString() @safe const pure scope
    {
        import std.algorithm: map;
        import std.array: join;
        return () @trusted { return msgLines.map!(a => getOutputPrefix(file, line) ~ a).join("\n"); }();
    }

private:

    const string[] msgLines;

    string getOutputPrefix(in string file, in size_t line) @safe const pure
    {
        import std.conv: to;
        return "    " ~ file ~ ":" ~ line.to!string ~ " - ";
    }
}