From 8e0ca41eb54ea02cc4d1fa4e67fe80b06d80591a Mon Sep 17 00:00:00 2001 From: Cameron Ross Date: Fri, 22 Jun 2018 00:59:10 -0300 Subject: [PATCH] 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 --- examples/getting_started/main.d | 2 +- examples/representer/main.d | 2 +- examples/yaml_bench/yaml_bench.d | 2 +- examples/yaml_gen/yaml_gen.d | 12 +-- meson.build | 1 - source/dyaml/dumper.d | 94 +++++++--------------- source/dyaml/emitter.d | 72 +++++++---------- source/dyaml/event.d | 7 +- source/dyaml/loader.d | 2 +- source/dyaml/node.d | 27 +++---- source/dyaml/parser.d | 2 +- source/dyaml/representer.d | 18 ++--- source/dyaml/serializer.d | 10 +-- source/dyaml/stream.d | 134 ------------------------------- source/dyaml/test/emitter.d | 25 +++--- source/dyaml/test/inputoutput.d | 1 - source/dyaml/test/representer.d | 20 ++--- 17 files changed, 117 insertions(+), 314 deletions(-) delete mode 100644 source/dyaml/stream.d diff --git a/examples/getting_started/main.d b/examples/getting_started/main.d index 6bf575c..01af354 100644 --- a/examples/getting_started/main.d +++ b/examples/getting_started/main.d @@ -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); } diff --git a/examples/representer/main.d b/examples/representer/main.d index 1e06091..0ab4d5d 100644 --- a/examples/representer/main.d +++ b/examples/representer/main.d @@ -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; diff --git a/examples/yaml_bench/yaml_bench.d b/examples/yaml_bench/yaml_bench.d index a0ec76c..83b4798 100644 --- a/examples/yaml_bench/yaml_bench.d +++ b/examples/yaml_bench/yaml_bench.d @@ -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 diff --git a/examples/yaml_gen/yaml_gen.d b/examples/yaml_gen/yaml_gen.d index 48e0408..e66209f 100644 --- a/examples/yaml_gen/yaml_gen.d +++ b/examples/yaml_gen/yaml_gen.d @@ -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) { diff --git a/meson.build b/meson.build index ae95b03..1b4c844 100644 --- a/meson.build +++ b/meson.build @@ -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', diff --git a/source/dyaml/dumper.d b/source/dyaml/dumper.d index 16e4277..6d8b810 100644 --- a/source/dyaml/dumper.d +++ b/source/dyaml/dumper.d @@ -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; diff --git a/source/dyaml/emitter.d b/source/dyaml/emitter.d index 35c0903..ea6bbf1 100644 --- a/source/dyaml/emitter.d +++ b/source/dyaml/emitter.d @@ -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 { - case Encoding.UTF_8: - stream_.writeExact(str); - break; - case Encoding.UTF_16: + static if(is(CharType == char)) + { + copy(str, stream_); + } + static if(is(CharType == wchar)) + { 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_) { diff --git a/source/dyaml/event.d b/source/dyaml/event.d index 061cb20..b8c7234 100644 --- a/source/dyaml/event.d +++ b/source/dyaml/event.d @@ -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; } diff --git a/source/dyaml/loader.d b/source/dyaml/loader.d index 54e2d0e..58e37f0 100644 --- a/source/dyaml/loader.d +++ b/source/dyaml/loader.d @@ -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); } } diff --git a/source/dyaml/node.d b/source/dyaml/node.d index 010c303..a05bbff 100644 --- a/source/dyaml/node.d +++ b/source/dyaml/node.d @@ -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; diff --git a/source/dyaml/parser.d b/source/dyaml/parser.d index 63a143d..d792d3d 100644 --- a/source/dyaml/parser.d +++ b/source/dyaml/parser.d @@ -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. diff --git a/source/dyaml/representer.d b/source/dyaml/representer.d index f31cd78..a4ebc88 100644 --- a/source/dyaml/representer.d +++ b/source/dyaml/representer.d @@ -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; diff --git a/source/dyaml/serializer.d b/source/dyaml/serializer.d index 7a84d4f..626943d 100644 --- a/source/dyaml/serializer.d +++ b/source/dyaml/serializer.d @@ -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. diff --git a/source/dyaml/stream.d b/source/dyaml/stream.d deleted file mode 100644 index 27ae25d..0000000 --- a/source/dyaml/stream.d +++ /dev/null @@ -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"); -} diff --git a/source/dyaml/test/emitter.d b/source/dyaml/test/emitter.d index c6fde44..ec0d69d 100644 --- a/source/dyaml/test/emitter.d +++ b/source/dyaml/test/emitter.d @@ -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; diff --git a/source/dyaml/test/inputoutput.d b/source/dyaml/test/inputoutput.d index 41fb474..61cd615 100644 --- a/source/dyaml/test/inputoutput.d +++ b/source/dyaml/test/inputoutput.d @@ -15,7 +15,6 @@ import std.file; import std.system; import dyaml.test.common; -import dyaml.stream; /// Get an UTF-16 byte order mark. /// diff --git a/source/dyaml/test/representer.d b/source/dyaml/test/representer.d index 6ef3ace..ac9f5c3 100644 --- a/source/dyaml/test/representer.d +++ b/source/dyaml/test/representer.d @@ -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();