Extend the functionality of formattedRead to permit a std.file.slurp like execution.

Template argument types can now be passed to formattedRead along with a
format string to parse and read the input range as a Tuple of those arguments.
All arguments must be read successfully, otherwise, and unlike std.file.slurp
which has non exhaustive option for partial reads, it'll throw a std.format.FormatException.

---
import std.exception : assertThrown;
import std.format : FormatException;
import std.typecons : tuple;

@safe pure unittest
{
    auto complete = "hello!34.5:124".formattedRead!(string, double, int)("%s!%s:%s");
    assert(complete == tuple("hello", 34.5, 124));

    assertThrown!FormatException("hello!34.5:".formattedRead!(string, double, int)("%s!%s:%s"));
}

/// The format string can be checked at compile-time:
@safe pure unittest
{
    auto expected = tuple("hello", 124, 34.5);
    auto result = "hello!124:34.5".formattedRead!("%s!%s:%s", string, int, double);
    assert(result == expected);

    assertThrown!FormatException("hello!34.5:".formattedRead!("%s!%s:%s", string, double, int));
}
---
