Implement range interface for Loader, remove old opApply interface (#196)
* Implement range interface for Loader, remove old opApply interface * remove dead Composer.getSingleNode * add Returns: lines to range primitives * a ddoc fixup
This commit is contained in:
parent
9c2ae02792
commit
1f5eb76996
|
@ -45,6 +45,7 @@ void extract(ref Node document) @safe
|
||||||
|
|
||||||
void main(string[] args) //@safe
|
void main(string[] args) //@safe
|
||||||
{
|
{
|
||||||
|
import std.array : array;
|
||||||
bool get = false;
|
bool get = false;
|
||||||
bool dump = false;
|
bool dump = false;
|
||||||
bool reload = false;
|
bool reload = false;
|
||||||
|
@ -113,7 +114,7 @@ void main(string[] args) //@safe
|
||||||
|
|
||||||
loader.resolver = resolver;
|
loader.resolver = resolver;
|
||||||
loader.constructor = constructor;
|
loader.constructor = constructor;
|
||||||
nodes = loader.loadAll();
|
nodes = loader.array;
|
||||||
}
|
}
|
||||||
void runDumpBenchmark() @safe
|
void runDumpBenchmark() @safe
|
||||||
{
|
{
|
||||||
|
|
|
@ -114,27 +114,6 @@ final class Composer
|
||||||
return composeDocument();
|
return composeDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
///Get single YAML document, throwing if there is more than one document.
|
|
||||||
Node getSingleNode() @safe
|
|
||||||
{
|
|
||||||
assert(parser_.front.id != EventID.streamEnd,
|
|
||||||
"Trying to get a node from Composer when there is no node to " ~
|
|
||||||
"get. use checkNode() to determine if there is a node.");
|
|
||||||
|
|
||||||
Node document = composeDocument();
|
|
||||||
|
|
||||||
//Ensure that the stream contains no more documents.
|
|
||||||
enforce(parser_.front.id == EventID.streamEnd,
|
|
||||||
new ComposerException("Expected single document in the stream, " ~
|
|
||||||
"but found another document.",
|
|
||||||
parser_.front.startMark));
|
|
||||||
|
|
||||||
skipExpected(EventID.streamEnd);
|
|
||||||
assert(parser_.empty, "Found event after stream end");
|
|
||||||
|
|
||||||
return document;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void skipExpected(const EventID id) @safe
|
void skipExpected(const EventID id) @safe
|
||||||
|
|
|
@ -47,6 +47,10 @@ struct Loader
|
||||||
string name_ = "<unknown>";
|
string name_ = "<unknown>";
|
||||||
// Are we done loading?
|
// Are we done loading?
|
||||||
bool done_;
|
bool done_;
|
||||||
|
// Last node read from stream
|
||||||
|
Node currentNode;
|
||||||
|
// Has the range interface been initialized yet?
|
||||||
|
bool rangeInitialized;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@disable this();
|
@disable this();
|
||||||
|
@ -191,25 +195,12 @@ struct Loader
|
||||||
* or on a YAML parsing error.
|
* or on a YAML parsing error.
|
||||||
*/
|
*/
|
||||||
Node load() @safe
|
Node load() @safe
|
||||||
in
|
|
||||||
{
|
{
|
||||||
assert(!done_, "Loader: Trying to load YAML twice");
|
enforce!YAMLException(!empty, "Zero documents in stream");
|
||||||
}
|
auto output = front;
|
||||||
do
|
popFront();
|
||||||
{
|
enforce!YAMLException(empty, "More than one document in stream");
|
||||||
try
|
return output;
|
||||||
{
|
|
||||||
lazyInitConstructorResolver();
|
|
||||||
scope(exit) { done_ = true; }
|
|
||||||
auto composer = new Composer(parser_, resolver_, constructor_);
|
|
||||||
enforce(composer.checkNode(), new YAMLException("No YAML document to load"));
|
|
||||||
return composer.getSingleNode();
|
|
||||||
}
|
|
||||||
catch(YAMLException e)
|
|
||||||
{
|
|
||||||
throw new YAMLException("Unable to load YAML from %s : %s"
|
|
||||||
.format(name_, e.msg), e.file, e.line);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load all YAML documents.
|
/** Load all YAML documents.
|
||||||
|
@ -224,63 +215,63 @@ struct Loader
|
||||||
*
|
*
|
||||||
* Throws: YAMLException on a parsing error.
|
* Throws: YAMLException on a parsing error.
|
||||||
*/
|
*/
|
||||||
Node[] loadAll() @safe
|
deprecated("Redundant, loader is now an InputRange") Node[] loadAll() @safe
|
||||||
{
|
{
|
||||||
Node[] nodes;
|
import std.array: array;
|
||||||
foreach(ref node; this)
|
return this.array;
|
||||||
|
}
|
||||||
|
/** Implements the empty range primitive.
|
||||||
|
*
|
||||||
|
* If there's no more documents left in the stream, this will be true.
|
||||||
|
*
|
||||||
|
* Returns: `true` if no more documents left, `false` otherwise.
|
||||||
|
*/
|
||||||
|
bool empty() @safe
|
||||||
|
{
|
||||||
|
// currentNode and done_ are both invalid until popFront is called once
|
||||||
|
if (!rangeInitialized)
|
||||||
{
|
{
|
||||||
nodes ~= node;
|
popFront();
|
||||||
}
|
}
|
||||||
return nodes;
|
return done_;
|
||||||
}
|
}
|
||||||
|
/** Implements the popFront range primitive.
|
||||||
/** Foreach over YAML documents.
|
*
|
||||||
*
|
* Reads the next document from the stream, if possible.
|
||||||
* Parses documents lazily, when they are needed.
|
*/
|
||||||
*
|
void popFront() @safe
|
||||||
* Foreach over a Loader can only be used once; this is enforced by contract.
|
|
||||||
*
|
|
||||||
* Throws: YAMLException on a parsing error.
|
|
||||||
*/
|
|
||||||
int opApply(int delegate(ref Node) @safe dg) @safe
|
|
||||||
{
|
{
|
||||||
return opApplyImpl(dg);
|
// Composer initialization is done here in case the constructor is
|
||||||
}
|
// modified, which is a pretty common case.
|
||||||
/// Ditto
|
static Composer composer;
|
||||||
int opApply(int delegate(ref Node) @system dg) @system
|
if (!rangeInitialized)
|
||||||
{
|
|
||||||
return opApplyImpl(dg);
|
|
||||||
}
|
|
||||||
|
|
||||||
package:
|
|
||||||
int opApplyImpl(T)(T dg)
|
|
||||||
in
|
|
||||||
{
|
|
||||||
assert(!done_, "Loader: Trying to load YAML twice");
|
|
||||||
}
|
|
||||||
do
|
|
||||||
{
|
|
||||||
scope(exit) { done_ = true; }
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
lazyInitConstructorResolver();
|
lazyInitConstructorResolver();
|
||||||
auto composer = new Composer(parser_, resolver_, constructor_);
|
composer = new Composer(parser_, resolver_, constructor_);
|
||||||
|
rangeInitialized = true;
|
||||||
int result;
|
|
||||||
while(composer.checkNode())
|
|
||||||
{
|
|
||||||
auto node = composer.getNode();
|
|
||||||
result = dg(node);
|
|
||||||
if(result) { break; }
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
catch(YAMLException e)
|
assert(!done_, "Loader.popFront called on empty range");
|
||||||
|
if (composer.checkNode())
|
||||||
{
|
{
|
||||||
throw new YAMLException("Unable to load YAML from %s : %s "
|
currentNode = composer.getNode();
|
||||||
.format(name_, e.msg), e.file, e.line);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
done_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Implements the front range primitive.
|
||||||
|
*
|
||||||
|
* Returns: the current document as a Node.
|
||||||
|
*/
|
||||||
|
Node front() @safe
|
||||||
|
{
|
||||||
|
// currentNode and done_ are both invalid until popFront is called once
|
||||||
|
if (!rangeInitialized)
|
||||||
|
{
|
||||||
|
popFront();
|
||||||
|
}
|
||||||
|
return currentNode;
|
||||||
}
|
}
|
||||||
// Scan and return all tokens. Used for debugging.
|
// Scan and return all tokens. Used for debugging.
|
||||||
Token[] scan() @safe
|
Token[] scan() @safe
|
||||||
|
@ -353,6 +344,7 @@ struct Loader
|
||||||
/// Load all YAML documents from a file:
|
/// Load all YAML documents from a file:
|
||||||
@safe unittest
|
@safe unittest
|
||||||
{
|
{
|
||||||
|
import std.array : array;
|
||||||
import std.file : write;
|
import std.file : write;
|
||||||
write("example.yaml",
|
write("example.yaml",
|
||||||
"---\n"~
|
"---\n"~
|
||||||
|
@ -362,7 +354,7 @@ struct Loader
|
||||||
"Hello world 2!\n"~
|
"Hello world 2!\n"~
|
||||||
"...\n"
|
"...\n"
|
||||||
);
|
);
|
||||||
auto nodes = Loader.fromFile("example.yaml").loadAll();
|
auto nodes = Loader.fromFile("example.yaml").array;
|
||||||
assert(nodes.length == 2);
|
assert(nodes.length == 2);
|
||||||
}
|
}
|
||||||
/// Iterate over YAML documents in a file, lazily loading them:
|
/// Iterate over YAML documents in a file, lazily loading them:
|
||||||
|
|
|
@ -34,8 +34,9 @@ void testParser(string dataFilename, string canonicalFilename) @safe
|
||||||
/// canonicalFilename = Another file to load, in canonical YAML format.
|
/// canonicalFilename = Another file to load, in canonical YAML format.
|
||||||
void testLoader(string dataFilename, string canonicalFilename) @safe
|
void testLoader(string dataFilename, string canonicalFilename) @safe
|
||||||
{
|
{
|
||||||
auto data = Loader.fromFile(dataFilename).loadAll();
|
import std.array : array;
|
||||||
auto canonical = Loader.fromFile(canonicalFilename).loadAll();
|
auto data = Loader.fromFile(dataFilename).array;
|
||||||
|
auto canonical = Loader.fromFile(canonicalFilename).array;
|
||||||
|
|
||||||
assert(data.length == canonical.length, "Unequal node count");
|
assert(data.length == canonical.length, "Unequal node count");
|
||||||
foreach(n; 0 .. data.length)
|
foreach(n; 0 .. data.length)
|
||||||
|
|
|
@ -20,8 +20,9 @@ import dyaml.test.common;
|
||||||
/// Params: errorFilename = File name to read from.
|
/// Params: errorFilename = File name to read from.
|
||||||
void testLoaderError(string errorFilename) @safe
|
void testLoaderError(string errorFilename) @safe
|
||||||
{
|
{
|
||||||
|
import std.array : array;
|
||||||
Node[] nodes;
|
Node[] nodes;
|
||||||
try { nodes = Loader.fromFile(errorFilename).loadAll(); }
|
try { nodes = Loader.fromFile(errorFilename).array; }
|
||||||
catch(YAMLException e)
|
catch(YAMLException e)
|
||||||
{
|
{
|
||||||
printException(e);
|
printException(e);
|
||||||
|
@ -35,9 +36,10 @@ void testLoaderError(string errorFilename) @safe
|
||||||
/// Params: errorFilename = File name to read from.
|
/// Params: errorFilename = File name to read from.
|
||||||
void testLoaderErrorString(string errorFilename) @safe
|
void testLoaderErrorString(string errorFilename) @safe
|
||||||
{
|
{
|
||||||
|
import std.array : array;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto nodes = Loader.fromFile(errorFilename).loadAll();
|
auto nodes = Loader.fromFile(errorFilename).array;
|
||||||
}
|
}
|
||||||
catch(YAMLException e)
|
catch(YAMLException e)
|
||||||
{
|
{
|
||||||
|
@ -52,7 +54,8 @@ void testLoaderErrorString(string errorFilename) @safe
|
||||||
/// Params: errorFilename = File name to read from.
|
/// Params: errorFilename = File name to read from.
|
||||||
void testLoaderErrorFilename(string errorFilename) @safe
|
void testLoaderErrorFilename(string errorFilename) @safe
|
||||||
{
|
{
|
||||||
try { auto nodes = Loader.fromFile(errorFilename).loadAll(); }
|
import std.array : array;
|
||||||
|
try { auto nodes = Loader.fromFile(errorFilename).array; }
|
||||||
catch(YAMLException e)
|
catch(YAMLException e)
|
||||||
{
|
{
|
||||||
printException(e);
|
printException(e);
|
||||||
|
|
|
@ -68,7 +68,7 @@ void testRepresenterTypes(string codeFilename) @safe
|
||||||
auto loader = Loader.fromString(emitStream.data.toUTF8);
|
auto loader = Loader.fromString(emitStream.data.toUTF8);
|
||||||
loader.name = "TEST";
|
loader.name = "TEST";
|
||||||
loader.constructor = constructor;
|
loader.constructor = constructor;
|
||||||
readNodes = loader.loadAll();
|
readNodes = loader.array;
|
||||||
|
|
||||||
assert(expectedNodes.length == readNodes.length);
|
assert(expectedNodes.length == readNodes.length);
|
||||||
foreach(n; 0 .. expectedNodes.length)
|
foreach(n; 0 .. expectedNodes.length)
|
||||||
|
|
Loading…
Reference in a new issue