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); writeln("The answer is ", root["Answer"].as!int);
//Dump the loaded document to output.yaml. //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}"), resolver.addImplicitResolver("!color", std.regex.regex("[0-9a-fA-F]{6}"),
"0123456789abcdefABCDEF"); "0123456789abcdefABCDEF");
auto dumper = Dumper("output.yaml"); auto dumper = dumper(File("output.yaml", "w").lockingTextWriter);
dumper.representer = representer; dumper.representer = representer;
dumper.resolver = resolver; dumper.resolver = resolver;

View file

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

View file

@ -297,15 +297,17 @@ void main(string[] args)
//Generate and dump the nodes. //Generate and dump the nodes.
Node[] generated = generate(configFile); Node[] generated = generate(configFile);
auto dumper = Dumper(args[1]); auto dumper = dumper(File(args[1], "w").lockingTextWriter);
auto encoding = config["encoding"]; 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.indent = config["indent"].as!uint;
dumper.textWidth = config["text-width"].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) catch(YAMLException e)
{ {

View file

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

View file

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

View file

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

View file

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

View file

@ -156,7 +156,7 @@ struct Loader
catch(YAMLException e) catch(YAMLException e)
{ {
throw new YAMLException("Unable to open %s for YAML loading: %s" 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 @safe unittest
{ {
import dyaml.dumper; import dyaml.dumper;
import dyaml.stream; auto stream = new Appender!string();
auto stream = new YMemoryStream();
auto node = Node([1, 2, 3, 4, 5]); auto node = Node([1, 2, 3, 4, 5]);
node.setStyle(CollectionStyle.Block); node.setStyle(CollectionStyle.Block);
auto dumper = Dumper(stream); auto dumper = dumper(stream);
dumper.dump(node); dumper.dump(node);
} }
/// ///
@safe unittest @safe unittest
{ {
import dyaml.dumper; import dyaml.dumper;
import dyaml.stream; auto stream = new Appender!string();
auto stream = new YMemoryStream();
auto node = Node(4); auto node = Node(4);
node.setStyle(ScalarStyle.Literal); node.setStyle(ScalarStyle.Literal);
auto dumper = Dumper(stream); auto dumper = dumper(stream);
dumper.dump(node); dumper.dump(node);
} }
@safe unittest @safe unittest
@ -2039,12 +2037,11 @@ struct Node
@safe unittest @safe unittest
{ {
import dyaml.dumper; import dyaml.dumper;
import dyaml.stream;
{ {
auto stream = new YMemoryStream(); auto stream = new Appender!string();
auto node = Node([1, 2, 3, 4, 5]); auto node = Node([1, 2, 3, 4, 5]);
node.setStyle(CollectionStyle.Block); node.setStyle(CollectionStyle.Block);
auto dumper = Dumper(stream); auto dumper = dumper(stream);
dumper.explicitEnd = false; dumper.explicitEnd = false;
dumper.explicitStart = false; dumper.explicitStart = false;
dumper.YAMLVersion = null; dumper.YAMLVersion = null;
@ -2054,10 +2051,10 @@ struct Node
assert(stream.data[0] == '-'); assert(stream.data[0] == '-');
} }
{ {
auto stream = new YMemoryStream(); auto stream = new Appender!string();
auto node = Node([1, 2, 3, 4, 5]); auto node = Node([1, 2, 3, 4, 5]);
node.setStyle(CollectionStyle.Flow); node.setStyle(CollectionStyle.Flow);
auto dumper = Dumper(stream); auto dumper = dumper(stream);
dumper.explicitEnd = false; dumper.explicitEnd = false;
dumper.explicitStart = false; dumper.explicitStart = false;
dumper.YAMLVersion = null; dumper.YAMLVersion = null;
@ -2067,10 +2064,10 @@ struct Node
assert(stream.data[0] == '['); assert(stream.data[0] == '[');
} }
{ {
auto stream = new YMemoryStream(); auto stream = new Appender!string();
auto node = Node(1); auto node = Node(1);
node.setStyle(ScalarStyle.SingleQuoted); node.setStyle(ScalarStyle.SingleQuoted);
auto dumper = Dumper(stream); auto dumper = dumper(stream);
dumper.explicitEnd = false; dumper.explicitEnd = false;
dumper.explicitStart = false; dumper.explicitStart = false;
dumper.YAMLVersion = null; dumper.YAMLVersion = null;
@ -2079,10 +2076,10 @@ struct Node
assert(stream.data == "!!int '1'\n"); assert(stream.data == "!!int '1'\n");
} }
{ {
auto stream = new YMemoryStream(); auto stream = new Appender!string();
auto node = Node(1); auto node = Node(1);
node.setStyle(ScalarStyle.DoubleQuoted); node.setStyle(ScalarStyle.DoubleQuoted);
auto dumper = Dumper(stream); auto dumper = dumper(stream);
dumper.explicitEnd = false; dumper.explicitEnd = false;
dumper.explicitStart = false; dumper.explicitStart = false;
dumper.YAMLVersion = null; dumper.YAMLVersion = null;

View file

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

View file

@ -16,7 +16,6 @@ import std.format;
import std.typecons; import std.typecons;
import dyaml.emitter; import dyaml.emitter;
import dyaml.encoding;
import dyaml.event; import dyaml.event;
import dyaml.exception; import dyaml.exception;
import dyaml.node; import dyaml.node;
@ -28,11 +27,11 @@ import dyaml.token;
package: package:
///Serializes represented YAML nodes, generating events which are then emitted by Emitter. ///Serializes represented YAML nodes, generating events which are then emitted by Emitter.
struct Serializer struct Serializer(Range, CharType)
{ {
private: private:
///Emitter to emit events produced. ///Emitter to emit events produced.
Emitter* emitter_; Emitter!(Range, CharType)* emitter_;
///Resolver used to determine which tags are automaticaly resolvable. ///Resolver used to determine which tags are automaticaly resolvable.
Resolver resolver_; Resolver resolver_;
@ -60,13 +59,12 @@ struct Serializer
* *
* Params: emitter = Emitter to emit events produced. * Params: emitter = Emitter to emit events produced.
* resolver = Resolver used to determine which tags are automaticaly resolvable. * 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? * explicitStart = Do all document starts have to be specified explicitly?
* explicitEnd = Do all document ends have to be specified explicitly? * explicitEnd = Do all document ends have to be specified explicitly?
* YAMLVersion = YAML version string. * YAMLVersion = YAML version string.
* tagDirectives = Tag directives to emit. * 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!"explicitStart" explicitStart,
const Flag!"explicitEnd" explicitEnd, string YAMLVersion, const Flag!"explicitEnd" explicitEnd, string YAMLVersion,
TagDirective[] tagDirectives) @safe TagDirective[] tagDirectives) @safe
@ -78,7 +76,7 @@ struct Serializer
YAMLVersion_ = YAMLVersion; YAMLVersion_ = YAMLVersion;
tagDirectives_ = tagDirectives; tagDirectives_ = tagDirectives;
emitter_.emit(streamStartEvent(Mark(), Mark(), encoding)); emitter_.emit(streamStartEvent(Mark(), Mark()));
} }
///Destroy the Serializer. ///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.range;
import std.typecons; import std.typecons;
import dyaml.stream;
import dyaml.dumper; import dyaml.dumper;
import dyaml.event; import dyaml.event;
import dyaml.test.common; import dyaml.test.common;
@ -83,17 +82,17 @@ void testEmitterOnData(string dataFilename, string canonicalFilename) @safe
//Must exist due to Anchor, Tags reference counts. //Must exist due to Anchor, Tags reference counts.
auto loader = Loader.fromFile(dataFilename); auto loader = Loader.fromFile(dataFilename);
auto events = loader.parse(); auto events = loader.parse();
auto emitStream = new YMemoryStream; auto emitStream = new Appender!string;
Dumper(emitStream).emit(events); dumper(emitStream).emit(events);
static if(verbose) static if(verbose)
{ {
writeln(dataFilename); writeln(dataFilename);
writeln("ORIGINAL:\n", readText(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.name = "TEST";
loader2.constructor = new Constructor; loader2.constructor = new Constructor;
loader2.resolver = new Resolver; loader2.resolver = new Resolver;
@ -113,16 +112,16 @@ void testEmitterOnCanonical(string canonicalFilename) @safe
auto events = loader.parse(); auto events = loader.parse();
foreach(canonical; [false, true]) foreach(canonical; [false, true])
{ {
auto emitStream = new YMemoryStream; auto emitStream = new Appender!string;
auto dumper = Dumper(emitStream); auto dumper = dumper(emitStream);
dumper.canonical = canonical; dumper.canonical = canonical;
dumper.emit(events); dumper.emit(events);
static if(verbose) static if(verbose)
{ {
writeln("OUTPUT (canonical=", canonical, "):\n", 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.name = "TEST";
loader2.constructor = new Constructor; loader2.constructor = new Constructor;
loader2.resolver = new Resolver; loader2.resolver = new Resolver;
@ -172,15 +171,15 @@ void testEmitterStyles(string dataFilename, string canonicalFilename) @safe
} }
styledEvents ~= event; styledEvents ~= event;
} }
auto emitStream = new YMemoryStream; auto emitStream = new Appender!string;
Dumper(emitStream).emit(styledEvents); dumper(emitStream).emit(styledEvents);
static if(verbose) static if(verbose)
{ {
writeln("OUTPUT (", filename, ", ", to!string(flowStyle), ", ", writeln("OUTPUT (", filename, ", ", to!string(flowStyle), ", ",
to!string(style), ")"); 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.name = "TEST";
loader2.constructor = new Constructor; loader2.constructor = new Constructor;
loader2.resolver = new Resolver; loader2.resolver = new Resolver;

View file

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

View file

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