mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 13:40:20 +03:00
Add 'std.format.read.formattedRead' overloads to return a Tuple with values read (#8647)
* refactor(formattedRead): add not is type validation to template argument 'fmt' Signed-off-by: João Lourenço <jlourenco5691@gmail.com> * feat(formattedRead): add overloads to return a tuple with the read values Signed-off-by: João Lourenço <jlourenco5691@gmail.com> * test(formattedRead): add unnittests for the tuple return type overloads Signed-off-by: João Lourenço <jlourenco5691@gmail.com> * chore(changelog): add a changelog formattedRead entry Signed-off-by: João Lourenço <jlourenco5691@gmail.com> --------- Signed-off-by: João Lourenço <jlourenco5691@gmail.com>
This commit is contained in:
parent
5d16dafff0
commit
6b666971bc
2 changed files with 143 additions and 2 deletions
30
changelog/formatted_read_tuple_return.dd
Normal file
30
changelog/formatted_read_tuple_return.dd
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
---
|
|
@ -198,7 +198,8 @@ module std.format.read;
|
||||||
|
|
||||||
import std.format.spec : FormatSpec;
|
import std.format.spec : FormatSpec;
|
||||||
import std.format.internal.read;
|
import std.format.internal.read;
|
||||||
import std.traits : isSomeString;
|
import std.meta : allSatisfy;
|
||||||
|
import std.traits : isSomeString, isType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reads an input range according to a format string and stores the read
|
Reads an input range according to a format string and stores the read
|
||||||
|
@ -300,7 +301,7 @@ uint formattedRead(Range, Char, Args...)(auto ref Range r, const(Char)[] fmt, au
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args)
|
uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args)
|
||||||
if (isSomeString!(typeof(fmt)))
|
if (!isType!fmt && isSomeString!(typeof(fmt)))
|
||||||
{
|
{
|
||||||
import std.format : checkFormatException;
|
import std.format : checkFormatException;
|
||||||
import std.meta : staticMap;
|
import std.meta : staticMap;
|
||||||
|
@ -692,6 +693,116 @@ if (isSomeString!(typeof(fmt)))
|
||||||
assert(aa2 == ["hello":1, "world":2]);
|
assert(aa2 == ["hello":1, "world":2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Reads an input range according to a format string and returns a tuple of Args
|
||||||
|
with the read values.
|
||||||
|
|
||||||
|
Format specifiers with format character $(B 'd'), $(B 'u') and $(B
|
||||||
|
'c') can take a $(B '*') parameter for skipping values.
|
||||||
|
|
||||||
|
The second version of `formattedRead` takes the format string as
|
||||||
|
template argument. In this case, it is checked for consistency at
|
||||||
|
compile-time.
|
||||||
|
|
||||||
|
Params:
|
||||||
|
Args = a variadic list of types of the arguments
|
||||||
|
*/
|
||||||
|
template formattedRead(Args...)
|
||||||
|
if (Args.length && allSatisfy!(isType, Args))
|
||||||
|
{
|
||||||
|
import std.typecons : Tuple;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Params:
|
||||||
|
r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
|
||||||
|
where the formatted input is read from
|
||||||
|
fmt = a $(MREF_ALTTEXT format string, std,format)
|
||||||
|
Range = the type of the input range `r`
|
||||||
|
Char = the character type used for `fmt`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A Tuple!Args with the elements filled.
|
||||||
|
|
||||||
|
Throws:
|
||||||
|
A $(REF_ALTTEXT FormatException, FormatException, std, format)
|
||||||
|
if reading did not succeed.
|
||||||
|
*/
|
||||||
|
Tuple!Args formattedRead(Range, Char)(auto ref Range r, const(Char)[] fmt)
|
||||||
|
{
|
||||||
|
import core.lifetime : forward;
|
||||||
|
import std.format : enforceFmt;
|
||||||
|
|
||||||
|
Tuple!Args args;
|
||||||
|
const numArgsFilled = .formattedRead(forward!r, fmt, args.expand);
|
||||||
|
enforceFmt(numArgsFilled == Args.length, "Failed reading into all format arguments");
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@safe pure unittest
|
||||||
|
{
|
||||||
|
import std.exception : assertThrown;
|
||||||
|
import std.format : FormatException;
|
||||||
|
import std.typecons : tuple;
|
||||||
|
|
||||||
|
auto complete = "hello!34.5:124".formattedRead!(string, double, int)("%s!%s:%s");
|
||||||
|
assert(complete == tuple("hello", 34.5, 124));
|
||||||
|
|
||||||
|
// reading ends early
|
||||||
|
assertThrown!FormatException("hello!34.5:".formattedRead!(string, double, int)("%s!%s:%s"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Skipping values
|
||||||
|
@safe pure unittest
|
||||||
|
{
|
||||||
|
import std.format : FormatException;
|
||||||
|
import std.typecons : tuple;
|
||||||
|
|
||||||
|
auto result = "orange: (12%) 15.25".formattedRead!(string, double)("%s: (%*d%%) %f");
|
||||||
|
assert(result == tuple("orange", 15.25));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
template formattedRead(alias fmt, Args...)
|
||||||
|
if (!isType!fmt && isSomeString!(typeof(fmt)) && Args.length && allSatisfy!(isType, Args))
|
||||||
|
{
|
||||||
|
import std.typecons : Flag, Tuple, Yes;
|
||||||
|
Tuple!Args formattedRead(Range)(auto ref Range r)
|
||||||
|
{
|
||||||
|
import core.lifetime : forward;
|
||||||
|
import std.format : enforceFmt;
|
||||||
|
|
||||||
|
Tuple!Args args;
|
||||||
|
const numArgsFilled = .formattedRead!fmt(forward!r, args.expand);
|
||||||
|
enforceFmt(numArgsFilled == Args.length, "Failed reading into all format arguments");
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The format string can be checked at compile-time
|
||||||
|
@safe pure unittest
|
||||||
|
{
|
||||||
|
import std.exception : assertThrown;
|
||||||
|
import std.format : FormatException;
|
||||||
|
import std.typecons : tuple;
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile-time consistency check
|
||||||
|
@safe pure unittest
|
||||||
|
{
|
||||||
|
import std.format : FormatException;
|
||||||
|
import std.typecons : tuple;
|
||||||
|
|
||||||
|
static assert(!__traits(compiles, "orange: (12%) 15.25".formattedRead!("%s: (%*d%%) %f", string, double)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reads a value from the given _input range and converts it according to a
|
Reads a value from the given _input range and converts it according to a
|
||||||
format specifier.
|
format specifier.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue