aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ext_depends/D-YAML/source/dyaml/serializer.d
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext_depends/D-YAML/source/dyaml/serializer.d')
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/serializer.d322
1 files changed, 322 insertions, 0 deletions
diff --git a/src/ext_depends/D-YAML/source/dyaml/serializer.d b/src/ext_depends/D-YAML/source/dyaml/serializer.d
new file mode 100644
index 0000000..4100cf3
--- /dev/null
+++ b/src/ext_depends/D-YAML/source/dyaml/serializer.d
@@ -0,0 +1,322 @@
+
+// Copyright Ferdinand Majerech 2011.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+/**
+ * YAML serializer.
+ * Code based on PyYAML: http://www.pyyaml.org
+ */
+module dyaml.serializer;
+
+
+import std.array;
+import std.format;
+import std.typecons;
+
+import dyaml.emitter;
+import dyaml.event;
+import dyaml.exception;
+import dyaml.node;
+import dyaml.resolver;
+import dyaml.tagdirective;
+import dyaml.token;
+
+
+package:
+
+///Serializes represented YAML nodes, generating events which are then emitted by Emitter.
+struct Serializer
+{
+ private:
+ ///Resolver used to determine which tags are automaticaly resolvable.
+ Resolver resolver_;
+
+ ///Do all document starts have to be specified explicitly?
+ Flag!"explicitStart" explicitStart_;
+ ///Do all document ends have to be specified explicitly?
+ Flag!"explicitEnd" explicitEnd_;
+ ///YAML version string.
+ string YAMLVersion_;
+
+ ///Tag directives to emit.
+ TagDirective[] tagDirectives_;
+
+ //TODO Use something with more deterministic memory usage.
+ ///Nodes with assigned anchors.
+ string[Node] anchors_;
+ ///Nodes with assigned anchors that are already serialized.
+ bool[Node] serializedNodes_;
+ ///ID of the last anchor generated.
+ uint lastAnchorID_ = 0;
+
+ public:
+ /**
+ * Construct a Serializer.
+ *
+ * Params:
+ * resolver = Resolver used to determine which tags are automaticaly resolvable.
+ * 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(Resolver resolver,
+ const Flag!"explicitStart" explicitStart,
+ const Flag!"explicitEnd" explicitEnd, string YAMLVersion,
+ TagDirective[] tagDirectives) @safe
+ {
+ resolver_ = resolver;
+ explicitStart_ = explicitStart;
+ explicitEnd_ = explicitEnd;
+ YAMLVersion_ = YAMLVersion;
+ tagDirectives_ = tagDirectives;
+ }
+
+ ///Begin the stream.
+ void startStream(EmitterT)(ref EmitterT emitter) @safe
+ {
+ emitter.emit(streamStartEvent(Mark(), Mark()));
+ }
+
+ ///End the stream.
+ void endStream(EmitterT)(ref EmitterT emitter) @safe
+ {
+ emitter.emit(streamEndEvent(Mark(), Mark()));
+ }
+
+ ///Serialize a node, emitting it in the process.
+ void serialize(EmitterT)(ref EmitterT emitter, ref Node node) @safe
+ {
+ emitter.emit(documentStartEvent(Mark(), Mark(), explicitStart_,
+ YAMLVersion_, tagDirectives_));
+ anchorNode(node);
+ serializeNode(emitter, node);
+ emitter.emit(documentEndEvent(Mark(), Mark(), explicitEnd_));
+ serializedNodes_.destroy();
+ anchors_.destroy();
+ string[Node] emptyAnchors;
+ anchors_ = emptyAnchors;
+ lastAnchorID_ = 0;
+ }
+
+ private:
+ /**
+ * Determine if it's a good idea to add an anchor to a node.
+ *
+ * Used to prevent associating every single repeating scalar with an
+ * anchor/alias - only nodes long enough can use anchors.
+ *
+ * Params: node = Node to check for anchorability.
+ *
+ * Returns: True if the node is anchorable, false otherwise.
+ */
+ static bool anchorable(ref Node node) @safe
+ {
+ if(node.nodeID == NodeID.scalar)
+ {
+ return (node.type == NodeType.string) ? node.as!string.length > 64 :
+ (node.type == NodeType.binary) ? node.as!(ubyte[]).length > 64 :
+ false;
+ }
+ return node.length > 2;
+ }
+
+ @safe unittest
+ {
+ import std.string : representation;
+ auto shortString = "not much";
+ auto longString = "A fairly long string that would be a good idea to add an anchor to";
+ auto node1 = Node(shortString);
+ auto node2 = Node(shortString.representation.dup);
+ auto node3 = Node(longString);
+ auto node4 = Node(longString.representation.dup);
+ auto node5 = Node([node1]);
+ auto node6 = Node([node1, node2, node3, node4]);
+ assert(!anchorable(node1));
+ assert(!anchorable(node2));
+ assert(anchorable(node3));
+ assert(anchorable(node4));
+ assert(!anchorable(node5));
+ assert(anchorable(node6));
+ }
+
+ ///Add an anchor to the node if it's anchorable and not anchored yet.
+ void anchorNode(ref Node node) @safe
+ {
+ if(!anchorable(node)){return;}
+
+ if((node in anchors_) !is null)
+ {
+ if(anchors_[node] is null)
+ {
+ anchors_[node] = generateAnchor();
+ }
+ return;
+ }
+
+ anchors_.remove(node);
+ final switch (node.nodeID)
+ {
+ case NodeID.mapping:
+ foreach(ref Node key, ref Node value; node)
+ {
+ anchorNode(key);
+ anchorNode(value);
+ }
+ break;
+ case NodeID.sequence:
+ foreach(ref Node item; node)
+ {
+ anchorNode(item);
+ }
+ break;
+ case NodeID.invalid:
+ assert(0);
+ case NodeID.scalar:
+ }
+ }
+
+ ///Generate and return a new anchor.
+ string generateAnchor() @safe
+ {
+ ++lastAnchorID_;
+ auto appender = appender!string();
+ formattedWrite(appender, "id%03d", lastAnchorID_);
+ return appender.data;
+ }
+
+ ///Serialize a node and all its subnodes.
+ void serializeNode(EmitterT)(ref EmitterT emitter, ref Node node) @safe
+ {
+ //If the node has an anchor, emit an anchor (as aliasEvent) on the
+ //first occurrence, save it in serializedNodes_, and emit an alias
+ //if it reappears.
+ string aliased;
+ if(anchorable(node) && (node in anchors_) !is null)
+ {
+ aliased = anchors_[node];
+ if((node in serializedNodes_) !is null)
+ {
+ emitter.emit(aliasEvent(Mark(), Mark(), aliased));
+ return;
+ }
+ serializedNodes_[node] = true;
+ }
+ final switch (node.nodeID)
+ {
+ case NodeID.mapping:
+ const defaultTag = resolver_.defaultMappingTag;
+ const implicit = node.tag_ == defaultTag;
+ emitter.emit(mappingStartEvent(Mark(), Mark(), aliased, node.tag_,
+ implicit, node.collectionStyle));
+ foreach(ref Node key, ref Node value; node)
+ {
+ serializeNode(emitter, key);
+ serializeNode(emitter, value);
+ }
+ emitter.emit(mappingEndEvent(Mark(), Mark()));
+ return;
+ case NodeID.sequence:
+ const defaultTag = resolver_.defaultSequenceTag;
+ const implicit = node.tag_ == defaultTag;
+ emitter.emit(sequenceStartEvent(Mark(), Mark(), aliased, node.tag_,
+ implicit, node.collectionStyle));
+ foreach(ref Node item; node)
+ {
+ serializeNode(emitter, item);
+ }
+ emitter.emit(sequenceEndEvent(Mark(), Mark()));
+ return;
+ case NodeID.scalar:
+ assert(node.type == NodeType.string, "Scalar node type must be string before serialized");
+ auto value = node.as!string;
+ const detectedTag = resolver_.resolve(NodeID.scalar, null, value, true);
+ const bool isDetected = node.tag_ == detectedTag;
+
+ emitter.emit(scalarEvent(Mark(), Mark(), aliased, node.tag_,
+ isDetected, value, node.scalarStyle));
+ return;
+ case NodeID.invalid:
+ assert(0);
+ }
+ }
+}
+
+// Issue #244
+@safe unittest
+{
+ import dyaml.dumper : dumper;
+ auto node = Node([
+ Node.Pair(
+ Node(""),
+ Node([
+ Node([
+ Node.Pair(
+ Node("d"),
+ Node([
+ Node([
+ Node.Pair(
+ Node("c"),
+ Node("")
+ ),
+ Node.Pair(
+ Node("b"),
+ Node("")
+ ),
+ Node.Pair(
+ Node(""),
+ Node("")
+ )
+ ])
+ ])
+ ),
+ ]),
+ Node([
+ Node.Pair(
+ Node("d"),
+ Node([
+ Node(""),
+ Node(""),
+ Node([
+ Node.Pair(
+ Node("c"),
+ Node("")
+ ),
+ Node.Pair(
+ Node("b"),
+ Node("")
+ ),
+ Node.Pair(
+ Node(""),
+ Node("")
+ )
+ ])
+ ])
+ ),
+ Node.Pair(
+ Node("z"),
+ Node("")
+ ),
+ Node.Pair(
+ Node(""),
+ Node("")
+ )
+ ]),
+ Node("")
+ ])
+ ),
+ Node.Pair(
+ Node("g"),
+ Node("")
+ ),
+ Node.Pair(
+ Node("h"),
+ Node("")
+ ),
+ ]);
+
+ auto stream = appender!string();
+ dumper().dump(stream, node);
+}