Replace stream-based dumping interface with an outputrange-based interface (#154)

Replace stream-based dumping interface with an outputrange-based interface
merged-on-behalf-of: Cameron Ross <elpenguino@gmail.com>
This commit is contained in:
Cameron Ross 2018-06-22 00:59:10 -03:00 committed by The Dlang Bot
parent 8f9dafdef3
commit 8e0ca41eb5
17 changed files with 117 additions and 314 deletions

View file

@ -14,5 +14,5 @@ void main()
writeln("The answer is ", root["Answer"].as!int);
//Dump the loaded document to output.yaml.
Dumper("output.yaml").dump(root);
dumper(File("output.yaml", "w").lockingTextWriter).dump(root);
}

View file

@ -47,7 +47,7 @@ void main()
resolver.addImplicitResolver("!color", std.regex.regex("[0-9a-fA-F]{6}"),
"0123456789abcdefABCDEF");
auto dumper = Dumper("output.yaml");
auto dumper = dumper(File("output.yaml", "w").lockingTextWriter);
dumper.representer = representer;
dumper.resolver = resolver;

View file

@ -119,7 +119,7 @@ void main(string[] args) //@safe
{
if(dump)
{
Dumper(file ~ ".dump").dump(nodes);
dumper(File(file ~ ".dump", "w").lockingTextWriter).dump(nodes);
}
}
void runGetBenchmark() @safe

View file

@ -297,15 +297,17 @@ void main(string[] args)
//Generate and dump the nodes.
Node[] generated = generate(configFile);
auto dumper = Dumper(args[1]);
auto dumper = dumper(File(args[1], "w").lockingTextWriter);
auto encoding = config["encoding"];
dumper.encoding = encoding == "utf-16" ? Encoding.UTF_16:
encoding == "utf-32" ? Encoding.UTF_32:
Encoding.UTF_8;
dumper.indent = config["indent"].as!uint;
dumper.textWidth = config["text-width"].as!uint;
dumper.dump(generated);
switch(encoding.as!string)
{
case "utf-16": dumper.dump!wchar(generated); break;
case "utf-32": dumper.dump!dchar(generated); break;
default: dumper.dump!char(generated); break;
}
}
catch(YAMLException e)
{

View file

@ -31,7 +31,6 @@ dyaml_src = [
'source/dyaml/resolver.d',
'source/dyaml/scanner.d',
'source/dyaml/serializer.d',
'source/dyaml/stream.d',
'source/dyaml/style.d',
'source/dyaml/tagdirective.d',
'source/dyaml/test/common.d',

View file

@ -11,13 +11,11 @@
*/
module dyaml.dumper;
//import std.stream;
import std.array;
import std.range.primitives;
import std.typecons;
import dyaml.stream;
import dyaml.emitter;
import dyaml.encoding;
import dyaml.event;
import dyaml.exception;
import dyaml.linebreak;
@ -34,9 +32,15 @@ import dyaml.tagdirective;
* User specified Representer and/or Resolver can be used to support new
* tags / data types.
*
* Setters are provided to affect output details (style, encoding, etc.).
* Setters are provided to affect output details (style, etc.).
*/
struct Dumper
auto dumper(Range)(auto ref Range output)
if (isOutputRange!(Range, char) || isOutputRange!(Range, wchar) || isOutputRange!(Range, dchar))
{
return Dumper!Range(output);
}
struct Dumper(Range)
{
private:
//Resolver to resolve tags.
@ -45,9 +49,7 @@ struct Dumper
Representer representer_;
//Stream to write to.
YStream stream_;
//True if this Dumper owns stream_ and needs to destroy it in the destructor.
bool weOwnStream_;
Range stream_;
//Write scalars in canonical form?
bool canonical_;
@ -57,8 +59,6 @@ struct Dumper
uint textWidth_ = 80;
//Line break to use.
LineBreak lineBreak_ = LineBreak.Unix;
//Character encoding to use.
Encoding encoding_ = Encoding.UTF_8;
//YAML version string.
string YAMLVersion_ = "1.1";
//Tag directives to use.
@ -73,45 +73,21 @@ struct Dumper
public:
@disable this();
@disable bool opEquals(ref Dumper);
@disable int opCmp(ref Dumper);
@disable bool opEquals(ref Dumper!Range);
@disable int opCmp(ref Dumper!Range);
/**
* Construct a Dumper writing to a file.
*
* Params: filename = File name to write to.
*
* Throws: YAMLException if the file can not be dumped to (e.g. cannot be opened).
*/
this(string filename) @safe
{
name_ = filename;
//try{this(new File(filename, FileMode.OutNew));}
try{this(new YFile(filename));}
//catch(StreamException e)
catch(Exception e)
{
throw new YAMLException("Unable to open file " ~ filename ~
" for YAML dumping: " ~ e.msg);
}
// need to destroy the File we constructed.
weOwnStream_ = true;
}
///Construct a Dumper writing to a _stream. This is useful to e.g. write to memory.
this(YStream stream) @safe
this(Range stream) @safe
{
resolver_ = new Resolver();
representer_ = new Representer();
stream_ = stream;
}
///Destroy the Dumper.
@trusted ~this()
{
if(weOwnStream_) { destroy(stream_); }
}
///Set stream _name. Used in debugging messages.
@property void name(string name) pure @safe nothrow
{
@ -159,12 +135,6 @@ struct Dumper
lineBreak_ = lineBreak;
}
///Set character _encoding to use. UTF-8 by default.
@property void encoding(Encoding encoding) pure @safe nothrow
{
encoding_ = encoding;
}
///Always explicitly write document start?
@property void explicitStart(bool explicit) pure @safe nothrow
{
@ -218,7 +188,7 @@ struct Dumper
///
@safe unittest
{
Dumper dumper = Dumper("example.yaml");
auto dumper = dumper(new Appender!string());
string[string] directives;
directives["!short!"] = "tag:long.org,2011:";
//This will emit tags starting with "tag:long.org,2011"
@ -239,12 +209,13 @@ struct Dumper
* Throws: YAMLException on error (e.g. invalid nodes,
* unable to write to file/stream).
*/
void dump(Node[] documents ...) @safe
void dump(CharacterType = char)(Node[] documents ...) @trusted
if (isOutputRange!(Range, CharacterType))
{
try
{
auto emitter = new Emitter(stream_, canonical_, indent_, textWidth_, lineBreak_);
auto serializer = Serializer(emitter, resolver_, encoding_, explicitStart_,
auto emitter = new Emitter!(Range, CharacterType)(stream_, canonical_, indent_, textWidth_, lineBreak_);
auto serializer = Serializer!(Range, CharacterType)(emitter, resolver_, explicitStart_,
explicitEnd_, YAMLVersion_, tags_);
foreach(ref document; documents)
{
@ -266,11 +237,11 @@ struct Dumper
*
* Throws: YAMLException if unable to emit.
*/
void emit(Event[] events) @safe
void emit(CharacterType = char)(Event[] events) @safe
{
try
{
auto emitter = Emitter(stream_, canonical_, indent_, textWidth_, lineBreak_);
auto emitter = Emitter!(Range, CharacterType)(stream_, canonical_, indent_, textWidth_, lineBreak_);
foreach(ref event; events)
{
emitter.emit(event);
@ -287,24 +258,23 @@ struct Dumper
@safe unittest
{
auto node = Node([1, 2, 3, 4, 5]);
Dumper("example.yaml").dump(node);
dumper(new Appender!string()).dump(node);
}
///Write multiple YAML documents to a file
@safe unittest
{
auto node1 = Node([1, 2, 3, 4, 5]);
auto node2 = Node("This document contains only one string");
Dumper("example.yaml").dump(node1, node2);
dumper(new Appender!string()).dump(node1, node2);
//Or with an array:
Dumper("example.yaml").dump([node1, node2]);
dumper(new Appender!string()).dump([node1, node2]);
}
///Write to memory
@safe unittest
{
import dyaml.stream;
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node([1, 2, 3, 4, 5]);
Dumper(stream).dump(node);
dumper(stream).dump(node);
}
///Use a custom representer/resolver to support custom data types and/or implicit tags
@safe unittest
@ -313,7 +283,7 @@ struct Dumper
auto representer = new Representer();
auto resolver = new Resolver();
//Add representer functions / resolver expressions here...
auto dumper = Dumper("example.yaml");
auto dumper = dumper(new Appender!string());
dumper.representer = representer;
dumper.resolver = resolver;
dumper.dump(node);
@ -321,10 +291,9 @@ struct Dumper
// Explicit document start/end markers
@safe unittest
{
import dyaml.stream;
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node([1, 2, 3, 4, 5]);
auto dumper = Dumper(stream);
auto dumper = dumper(stream);
dumper.explicitEnd = true;
dumper.explicitStart = true;
dumper.YAMLVersion = null;
@ -337,10 +306,9 @@ struct Dumper
// No explicit document start/end markers
@safe unittest
{
import dyaml.stream;
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node([1, 2, 3, 4, 5]);
auto dumper = Dumper(stream);
auto dumper = dumper(stream);
dumper.explicitEnd = false;
dumper.explicitStart = false;
dumper.YAMLVersion = null;

View file

@ -23,7 +23,6 @@ import std.system;
import std.typecons;
import std.utf;
import dyaml.stream;
import dyaml.encoding;
import dyaml.escapes;
import dyaml.event;
@ -68,7 +67,7 @@ private alias isFlowIndicator = among!(',', '?', '[', ']', '{', '}');
private alias isSpace = among!('\0', '\n', '\r', '\u0085', '\u2028', '\u2029', ' ', '\t');
//Emits YAML events into a file/stream.
struct Emitter
struct Emitter(Range, CharType) if (isOutputRange!(Range, CharType))
{
private:
///Default tag handle shortcuts and replacements.
@ -76,9 +75,7 @@ struct Emitter
[TagDirective("!", "!"), TagDirective("!!", "tag:yaml.org,2002:")];
///Stream to write to.
YStream stream_;
///Encoding can be overriden by STREAM-START.
Encoding encoding_ = Encoding.UTF_8;
Range stream_;
///Stack of states.
Appender!(void function() @safe[]) states_;
@ -159,15 +156,13 @@ struct Emitter
/**
* Construct an emitter.
*
* Params: stream = YStream to write to. Must be writable.
* Params: stream = Output range to write to.
* canonical = Write scalars in canonical form?
* indent = Indentation width.
* lineBreak = Line break character/s.
*/
this(YStream stream, const bool canonical, const int indent, const int width,
this(Range stream, const bool canonical, const int indent, const int width,
const LineBreak lineBreak) @trusted
in{assert(stream.writeable, "Can't emit YAML to a non-writable stream");}
body
{
states_.reserve(32);
indents_.reserve(32);
@ -224,19 +219,22 @@ struct Emitter
///Write a string to the file/stream.
void writeString(const char[] str) @safe
{
try final switch(encoding_)
try
{
static if(is(CharType == char))
{
copy(str, stream_);
}
static if(is(CharType == wchar))
{
case Encoding.UTF_8:
stream_.writeExact(str);
break;
case Encoding.UTF_16:
const buffer = to!wstring(str);
stream_.writeExact(buffer);
break;
case Encoding.UTF_32:
copy(buffer, stream_);
}
static if(is(CharType == dchar))
{
const buffer = to!dstring(str);
stream_.writeExact(buffer);
break;
copy(buffer, stream_);
}
}
catch(Exception e)
{
@ -319,7 +317,6 @@ struct Emitter
enforce(eventTypeIs(EventID.StreamStart),
new EmitterException("Expected YStreamStart, but got " ~ event_.idString));
encoding_ = event_.encoding;
writeStreamStart();
nextExpected(&expectDocumentStart!(Yes.first));
}
@ -411,7 +408,6 @@ struct Emitter
writeIndicator("...", Yes.needWhitespace);
writeIndent();
}
stream_.flush();
nextExpected(&expectDocumentStart!(No.first));
}
@ -749,7 +745,7 @@ struct Emitter
//{
// writeIndent();
//}
auto writer = ScalarWriter(this, analysis_.scalar,
auto writer = ScalarWriter!(Range, CharType)(this, analysis_.scalar,
context_ != Context.MappingSimpleKey);
with(writer) final switch(style_)
{
@ -1153,29 +1149,15 @@ struct Emitter
///Start the YAML stream (write the unicode byte order mark).
void writeStreamStart() @safe
{
immutable(ubyte)[] bom;
//Write BOM (except for UTF-8)
final switch(encoding_)
static if(is(CharType == wchar) || is(CharType == dchar))
{
case Encoding.UTF_8:
break;
case Encoding.UTF_16:
bom = std.system.endian == Endian.littleEndian
? bomTable[BOM.utf16le].sequence
: bomTable[BOM.utf16be].sequence;
break;
case Encoding.UTF_32:
bom = std.system.endian == Endian.littleEndian
? bomTable[BOM.utf32le].sequence
: bomTable[BOM.utf32be].sequence;
break;
stream_.put(cast(CharType)'\uFEFF');
}
enforce(stream_.write(bom) == bom.length, new EmitterException("Unable to write to stream"));
}
///End the YAML stream.
void writeStreamEnd() @safe {stream_.flush();}
void writeStreamEnd() @safe {}
///Write an indicator (e.g. ":", "[", ">", etc.).
void writeIndicator(const char[] indicator,
@ -1270,7 +1252,7 @@ struct Emitter
private:
///RAII struct used to write out scalar values.
struct ScalarWriter
struct ScalarWriter(Range, CharType)
{
invariant()
{
@ -1279,14 +1261,14 @@ struct ScalarWriter
}
private:
@disable int opCmp(ref Emitter);
@disable bool opEquals(ref Emitter);
@disable int opCmp(ref Emitter!(Range, CharType));
@disable bool opEquals(ref Emitter!(Range, CharType));
///Used as "null" UTF-32 character.
static immutable dcharNone = dchar.max;
///Emitter used to emit the scalar.
Emitter* emitter_;
Emitter!(Range, CharType)* emitter_;
///UTF-8 encoded text of the scalar to write.
string text_;
@ -1307,7 +1289,7 @@ struct ScalarWriter
public:
///Construct a ScalarWriter using emitter to output text.
this(ref Emitter emitter, string text, const bool split = true) @trusted nothrow
this(ref Emitter!(Range, CharType) emitter, string text, const bool split = true) @trusted nothrow
{
emitter_ = &emitter;
text_ = text;
@ -1502,7 +1484,7 @@ struct ScalarWriter
///Write text as plain scalar.
void writePlain() @safe
{
if(emitter_.context_ == Emitter.Context.Root){emitter_.openEnded_ = true;}
if(emitter_.context_ == Emitter!(Range, CharType).Context.Root){emitter_.openEnded_ = true;}
if(text_ == ""){return;}
if(!emitter_.whitespace_)
{

View file

@ -13,7 +13,6 @@ module dyaml.event;
import std.array;
import std.conv;
import dyaml.encoding;
import dyaml.exception;
import dyaml.reader;
import dyaml.tagdirective;
@ -80,8 +79,6 @@ struct Event
*/
bool explicitDocument;
}
///Encoding of the stream, if this is a StreamStart.
Encoding encoding;
///Collection style, if this is a SequenceStart or MappingStart.
CollectionStyle collectionStyle = CollectionStyle.Invalid;
@ -159,16 +156,14 @@ Event collectionStartEvent(EventID id)
*
* Params: start = Start position of the event in the file/stream.
* end = End position of the event in the file/stream.
* encoding = Encoding of the stream.
*/
Event streamStartEvent(const Mark start, const Mark end, const Encoding encoding)
Event streamStartEvent(const Mark start, const Mark end)
pure @safe nothrow
{
Event result;
result.startMark = start;
result.endMark = end;
result.id = EventID.StreamStart;
result.encoding = encoding;
return result;
}

View file

@ -156,7 +156,7 @@ struct Loader
catch(YAMLException e)
{
throw new YAMLException("Unable to open %s for YAML loading: %s"
.format(name_, e.msg));
.format(name_, e.msg), e.file, e.line);
}
}

View file

@ -2011,24 +2011,22 @@ struct Node
@safe unittest
{
import dyaml.dumper;
import dyaml.stream;
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node([1, 2, 3, 4, 5]);
node.setStyle(CollectionStyle.Block);
auto dumper = Dumper(stream);
auto dumper = dumper(stream);
dumper.dump(node);
}
///
@safe unittest
{
import dyaml.dumper;
import dyaml.stream;
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node(4);
node.setStyle(ScalarStyle.Literal);
auto dumper = Dumper(stream);
auto dumper = dumper(stream);
dumper.dump(node);
}
@safe unittest
@ -2039,12 +2037,11 @@ struct Node
@safe unittest
{
import dyaml.dumper;
import dyaml.stream;
{
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node([1, 2, 3, 4, 5]);
node.setStyle(CollectionStyle.Block);
auto dumper = Dumper(stream);
auto dumper = dumper(stream);
dumper.explicitEnd = false;
dumper.explicitStart = false;
dumper.YAMLVersion = null;
@ -2054,10 +2051,10 @@ struct Node
assert(stream.data[0] == '-');
}
{
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node([1, 2, 3, 4, 5]);
node.setStyle(CollectionStyle.Flow);
auto dumper = Dumper(stream);
auto dumper = dumper(stream);
dumper.explicitEnd = false;
dumper.explicitStart = false;
dumper.YAMLVersion = null;
@ -2067,10 +2064,10 @@ struct Node
assert(stream.data[0] == '[');
}
{
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node(1);
node.setStyle(ScalarStyle.SingleQuoted);
auto dumper = Dumper(stream);
auto dumper = dumper(stream);
dumper.explicitEnd = false;
dumper.explicitStart = false;
dumper.YAMLVersion = null;
@ -2079,10 +2076,10 @@ struct Node
assert(stream.data == "!!int '1'\n");
}
{
auto stream = new YMemoryStream();
auto stream = new Appender!string();
auto node = Node(1);
node.setStyle(ScalarStyle.DoubleQuoted);
auto dumper = Dumper(stream);
auto dumper = dumper(stream);
dumper.explicitEnd = false;
dumper.explicitStart = false;
dumper.YAMLVersion = null;

View file

@ -256,7 +256,7 @@ final class Parser
{
const token = scanner_.getToken();
state_ = &parseImplicitDocumentStart;
return streamStartEvent(token.startMark, token.endMark, token.encoding);
return streamStartEvent(token.startMark, token.endMark);
}
/// Parse implicit document start, unless explicit detected: if so, parse explicit.

View file

@ -154,7 +154,7 @@ final class Representer
return representer.representScalar("!mystruct.tag", scalar);
}
auto dumper = Dumper("example.yaml");
auto dumper = dumper(new Appender!string);
auto representer = new Representer;
representer.addRepresenter!MyStruct(&representMyStruct);
dumper.representer = representer;
@ -207,7 +207,7 @@ final class Representer
return representer.representScalar("!myclass.tag", scalar);
}
auto dumper = Dumper("example.yaml");
auto dumper = dumper(new Appender!string);
auto representer = new Representer;
representer.addRepresenter!MyClass(&representMyClass);
dumper.representer = representer;
@ -263,7 +263,7 @@ final class Representer
return representer.representScalar("!mystruct.tag", scalar);
}
auto dumper = Dumper("example.yaml");
auto dumper = dumper(new Appender!string);
auto representer = new Representer;
representer.addRepresenter!MyStruct(&representMyStruct);
dumper.representer = representer;
@ -339,7 +339,7 @@ final class Representer
CollectionStyle.Flow);
}
auto dumper = Dumper("example.yaml");
auto dumper = dumper(new Appender!string);
auto representer = new Representer;
representer.addRepresenter!MyStruct(&representMyStruct);
dumper.representer = representer;
@ -422,7 +422,7 @@ final class Representer
return representer.representMapping("!mystruct.tag", pairs);
}
auto dumper = Dumper("example.yaml");
auto dumper = dumper(new Appender!string);
auto representer = new Representer;
representer.addRepresenter!MyStruct(&representMyStruct);
dumper.representer = representer;
@ -457,7 +457,7 @@ final class Representer
}
//Represent a node, serializing with specified Serializer.
void represent(ref Serializer serializer, ref Node node) @safe
void represent(Range, CharType)(ref Serializer!(Range, CharType) serializer, ref Node node) @safe
{
auto data = representData(node);
serializer.serialize(data);
@ -677,15 +677,13 @@ Node representMyClass(ref Node node, Representer representer) @safe
return representer.representScalar("!myclass.tag", scalar);
}
import dyaml.stream;
@safe unittest
{
foreach(r; [&representMyStruct,
&representMyStructSeq,
&representMyStructMap])
{
auto dumper = Dumper(new YMemoryStream());
auto dumper = dumper(new Appender!string);
auto representer = new Representer;
representer.addRepresenter!MyStruct(r);
dumper.representer = representer;
@ -695,7 +693,7 @@ import dyaml.stream;
@safe unittest
{
auto dumper = Dumper(new YMemoryStream());
auto dumper = dumper(new Appender!string);
auto representer = new Representer;
representer.addRepresenter!MyClass(&representMyClass);
dumper.representer = representer;

View file

@ -16,7 +16,6 @@ import std.format;
import std.typecons;
import dyaml.emitter;
import dyaml.encoding;
import dyaml.event;
import dyaml.exception;
import dyaml.node;
@ -28,11 +27,11 @@ import dyaml.token;
package:
///Serializes represented YAML nodes, generating events which are then emitted by Emitter.
struct Serializer
struct Serializer(Range, CharType)
{
private:
///Emitter to emit events produced.
Emitter* emitter_;
Emitter!(Range, CharType)* emitter_;
///Resolver used to determine which tags are automaticaly resolvable.
Resolver resolver_;
@ -60,13 +59,12 @@ struct Serializer
*
* Params: emitter = Emitter to emit events produced.
* resolver = Resolver used to determine which tags are automaticaly resolvable.
* encoding = Character encoding to use.
* explicitStart = Do all document starts have to be specified explicitly?
* explicitEnd = Do all document ends have to be specified explicitly?
* YAMLVersion = YAML version string.
* tagDirectives = Tag directives to emit.
*/
this(Emitter* emitter, Resolver resolver, Encoding encoding,
this(Emitter!(Range, CharType)* emitter, Resolver resolver,
const Flag!"explicitStart" explicitStart,
const Flag!"explicitEnd" explicitEnd, string YAMLVersion,
TagDirective[] tagDirectives) @safe
@ -78,7 +76,7 @@ struct Serializer
YAMLVersion_ = YAMLVersion;
tagDirectives_ = tagDirectives;
emitter_.emit(streamStartEvent(Mark(), Mark(), encoding));
emitter_.emit(streamStartEvent(Mark(), Mark()));
}
///Destroy the Serializer.

View file

@ -1,134 +0,0 @@
module dyaml.stream;
interface YStream
{
void writeExact(const void* buffer, size_t size);
void writeExact(const void[] buffer) @safe;
size_t write(const(ubyte)[] buffer) @safe;
size_t write(const(char)[] str) @safe;
void flush() @safe;
@property bool writeable() @safe;
}
class YMemoryStream : YStream
{
ubyte[] data;
void writeExact(const void* buffer, size_t size)
{
data ~= cast(ubyte[])buffer[0 .. size];
}
void writeExact(const void[] buffer) @trusted
{
data ~= cast(ubyte[])buffer;
}
size_t write(const(ubyte)[] buffer) @safe
{
data ~= buffer;
return buffer.length;
}
size_t write(const(char)[] str) @safe
{
return write(cast(const(ubyte)[])str);
}
void flush() @safe {}
@property bool writeable() @safe { return true; }
}
class YFile : YStream
{
static import std.stdio;
std.stdio.File file;
this(string fn) @safe
{
this.file = std.stdio.File(fn, "w");
}
this(std.stdio.File file) @safe
{
this.file = file;
}
@system unittest
{
import std.stdio : File;
auto stream = new YFile(File.tmpfile);
stream.write("Test writing to tmpFile through YFile stream\n");
}
void writeExact(const void* buffer, size_t size)
{
this.file.rawWrite(cast(const) buffer[0 .. size]);
}
void writeExact(const void[] buffer) @trusted
{
this.file.rawWrite(buffer);
}
size_t write(const(ubyte)[] buffer) @trusted
{
this.file.rawWrite(buffer);
return buffer.length;
}
size_t write(const(char)[] str) @trusted
{
return write(cast(ubyte[])str);
}
void flush() @safe
{
this.file.flush();
}
@property bool writeable() @safe { return true; }
}
@safe unittest
{
import dyaml.dumper, dyaml.loader, dyaml.node;
import std.file : readText, remove;
string test = "Hello World : [Hello, World]\n" ~
"Answer: 42";
//Read the input.
Node expected = Loader.fromString(test).load();
assert(expected["Hello World"][0] == "Hello");
assert(expected["Hello World"][1] == "World");
assert(expected["Answer"].as!int == 42);
//Dump the loaded document to output.yaml.
Dumper("output.yaml").dump(expected);
// Load the file and verify that it was saved correctly.
Node actual = Loader.fromFile("output.yaml").load();
assert(actual["Hello World"][0] == "Hello");
assert(actual["Hello World"][1] == "World");
assert(actual["Answer"].as!int == 42);
assert(actual == expected);
// Clean up.
remove("output.yaml");
}
@safe unittest // #88, #89
{
import dyaml.dumper, dyaml.loader;
import std.file : remove, read;
enum fn = "output.yaml";
scope (exit) fn.remove;
auto dumper = Dumper(fn);
dumper.YAMLVersion = null; // supress directive
dumper.dump(Loader.fromString("Hello world").load);
assert (fn.read()[0..3] == "Hel");
}

View file

@ -15,7 +15,6 @@ import std.file;
import std.range;
import std.typecons;
import dyaml.stream;
import dyaml.dumper;
import dyaml.event;
import dyaml.test.common;
@ -83,17 +82,17 @@ void testEmitterOnData(string dataFilename, string canonicalFilename) @safe
//Must exist due to Anchor, Tags reference counts.
auto loader = Loader.fromFile(dataFilename);
auto events = loader.parse();
auto emitStream = new YMemoryStream;
Dumper(emitStream).emit(events);
auto emitStream = new Appender!string;
dumper(emitStream).emit(events);
static if(verbose)
{
writeln(dataFilename);
writeln("ORIGINAL:\n", readText(dataFilename));
writeln("OUTPUT:\n", cast(char[])emitStream.data);
writeln("OUTPUT:\n", emitStream.toString);
}
auto loader2 = Loader.fromBuffer(emitStream.data);
auto loader2 = Loader.fromString(emitStream.data);
loader2.name = "TEST";
loader2.constructor = new Constructor;
loader2.resolver = new Resolver;
@ -113,16 +112,16 @@ void testEmitterOnCanonical(string canonicalFilename) @safe
auto events = loader.parse();
foreach(canonical; [false, true])
{
auto emitStream = new YMemoryStream;
auto dumper = Dumper(emitStream);
auto emitStream = new Appender!string;
auto dumper = dumper(emitStream);
dumper.canonical = canonical;
dumper.emit(events);
static if(verbose)
{
writeln("OUTPUT (canonical=", canonical, "):\n",
cast(char[])emitStream.data);
emitStream.toString);
}
auto loader2 = Loader.fromBuffer(emitStream.data);
auto loader2 = Loader.fromString(emitStream.data);
loader2.name = "TEST";
loader2.constructor = new Constructor;
loader2.resolver = new Resolver;
@ -172,15 +171,15 @@ void testEmitterStyles(string dataFilename, string canonicalFilename) @safe
}
styledEvents ~= event;
}
auto emitStream = new YMemoryStream;
Dumper(emitStream).emit(styledEvents);
auto emitStream = new Appender!string;
dumper(emitStream).emit(styledEvents);
static if(verbose)
{
writeln("OUTPUT (", filename, ", ", to!string(flowStyle), ", ",
to!string(style), ")");
writeln(cast(char[])emitStream.data);
writeln(emitStream.toString);
}
auto loader2 = Loader.fromBuffer(emitStream.data);
auto loader2 = Loader.fromString(emitStream.data);
loader2.name = "TEST";
loader2.constructor = new Constructor;
loader2.resolver = new Resolver;

View file

@ -15,7 +15,6 @@ import std.file;
import std.system;
import dyaml.test.common;
import dyaml.stream;
/// Get an UTF-16 byte order mark.
///

View file

@ -10,9 +10,12 @@ module dyaml.test.representer;
version(unittest)
{
import std.path;
import std.array;
import std.exception;
import std.meta;
import std.path;
import std.typecons;
import std.utf;
import dyaml.test.common;
import dyaml.test.constructor;
@ -30,9 +33,9 @@ void testRepresenterTypes(string codeFilename) @safe
new Exception("Unimplemented representer test: " ~ baseName));
Node[] expectedNodes = expected[baseName];
foreach(encoding; [Encoding.UTF_8, Encoding.UTF_16, Encoding.UTF_32])
foreach(encoding; AliasSeq!(char, wchar, dchar))
{
ubyte[] output;
immutable(encoding)[] output;
Node[] readNodes;
scope(failure)
@ -49,23 +52,20 @@ void testRepresenterTypes(string codeFilename) @safe
}
}
import dyaml.stream;
auto emitStream = new YMemoryStream;
auto emitStream = new Appender!(immutable(encoding)[]);
auto representer = new Representer;
representer.addRepresenter!TestClass(&representClass);
representer.addRepresenter!TestStruct(&representStruct);
auto dumper = Dumper(emitStream);
auto dumper = dumper(emitStream);
dumper.representer = representer;
dumper.encoding = encoding;
dumper.dump(expectedNodes);
dumper.dump!encoding(expectedNodes);
output = emitStream.data;
auto constructor = new Constructor;
constructor.addConstructorMapping("!tag1", &constructClass);
constructor.addConstructorScalar("!tag2", &constructStruct);
auto loader = Loader.fromBuffer(emitStream.data);
auto loader = Loader.fromString(emitStream.data.toUTF8);
loader.name = "TEST";
loader.constructor = constructor;
readNodes = loader.loadAll();