Newer
Older
dub_jkp / source / dub / internal / dyaml / serializer.d
@WebFreak001 WebFreak001 on 4 Feb 2023 11 KB fix typo(s)
  1.  
  2. // Copyright Ferdinand Majerech 2011.
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6.  
  7. /**
  8. * YAML serializer.
  9. * Code based on PyYAML: http://www.pyyaml.org
  10. */
  11. module dub.internal.dyaml.serializer;
  12.  
  13.  
  14. import std.array;
  15. import std.format;
  16. import std.typecons;
  17.  
  18. import dub.internal.dyaml.emitter;
  19. import dub.internal.dyaml.event;
  20. import dub.internal.dyaml.exception;
  21. import dub.internal.dyaml.node;
  22. import dub.internal.dyaml.resolver;
  23. import dub.internal.dyaml.tagdirective;
  24. import dub.internal.dyaml.token;
  25.  
  26.  
  27. package:
  28.  
  29. ///Serializes represented YAML nodes, generating events which are then emitted by Emitter.
  30. struct Serializer
  31. {
  32. private:
  33. ///Resolver used to determine which tags are automatically resolvable.
  34. Resolver resolver_;
  35.  
  36. ///Do all document starts have to be specified explicitly?
  37. Flag!"explicitStart" explicitStart_;
  38. ///Do all document ends have to be specified explicitly?
  39. Flag!"explicitEnd" explicitEnd_;
  40. ///YAML version string.
  41. string YAMLVersion_;
  42.  
  43. ///Tag directives to emit.
  44. TagDirective[] tagDirectives_;
  45.  
  46. //TODO Use something with more deterministic memory usage.
  47. ///Nodes with assigned anchors.
  48. string[Node] anchors_;
  49. ///Nodes with assigned anchors that are already serialized.
  50. bool[Node] serializedNodes_;
  51. ///ID of the last anchor generated.
  52. uint lastAnchorID_ = 0;
  53.  
  54. public:
  55. /**
  56. * Construct a Serializer.
  57. *
  58. * Params:
  59. * resolver = Resolver used to determine which tags are automatically resolvable.
  60. * explicitStart = Do all document starts have to be specified explicitly?
  61. * explicitEnd = Do all document ends have to be specified explicitly?
  62. * YAMLVersion = YAML version string.
  63. * tagDirectives = Tag directives to emit.
  64. */
  65. this(Resolver resolver,
  66. const Flag!"explicitStart" explicitStart,
  67. const Flag!"explicitEnd" explicitEnd, string YAMLVersion,
  68. TagDirective[] tagDirectives) @safe
  69. {
  70. resolver_ = resolver;
  71. explicitStart_ = explicitStart;
  72. explicitEnd_ = explicitEnd;
  73. YAMLVersion_ = YAMLVersion;
  74. tagDirectives_ = tagDirectives;
  75. }
  76.  
  77. ///Begin the stream.
  78. void startStream(EmitterT)(ref EmitterT emitter) @safe
  79. {
  80. emitter.emit(streamStartEvent(Mark(), Mark()));
  81. }
  82.  
  83. ///End the stream.
  84. void endStream(EmitterT)(ref EmitterT emitter) @safe
  85. {
  86. emitter.emit(streamEndEvent(Mark(), Mark()));
  87. }
  88.  
  89. ///Serialize a node, emitting it in the process.
  90. void serialize(EmitterT)(ref EmitterT emitter, ref Node node) @safe
  91. {
  92. emitter.emit(documentStartEvent(Mark(), Mark(), explicitStart_,
  93. YAMLVersion_, tagDirectives_));
  94. anchorNode(node);
  95. serializeNode(emitter, node);
  96. emitter.emit(documentEndEvent(Mark(), Mark(), explicitEnd_));
  97. serializedNodes_.destroy();
  98. anchors_.destroy();
  99. string[Node] emptyAnchors;
  100. anchors_ = emptyAnchors;
  101. lastAnchorID_ = 0;
  102. }
  103.  
  104. private:
  105. /**
  106. * Determine if it's a good idea to add an anchor to a node.
  107. *
  108. * Used to prevent associating every single repeating scalar with an
  109. * anchor/alias - only nodes long enough can use anchors.
  110. *
  111. * Params: node = Node to check for anchor-ability.
  112. *
  113. * Returns: True if the node is anchor-able, false otherwise.
  114. */
  115. static bool anchorable(ref Node node) @safe
  116. {
  117. if(node.nodeID == NodeID.scalar)
  118. {
  119. return (node.type == NodeType.string) ? node.as!string.length > 64 :
  120. (node.type == NodeType.binary) ? node.as!(ubyte[]).length > 64 :
  121. false;
  122. }
  123. return node.length > 2;
  124. }
  125.  
  126. @safe unittest
  127. {
  128. import std.string : representation;
  129. auto shortString = "not much";
  130. auto longString = "A fairly long string that would be a good idea to add an anchor to";
  131. auto node1 = Node(shortString);
  132. auto node2 = Node(shortString.representation.dup);
  133. auto node3 = Node(longString);
  134. auto node4 = Node(longString.representation.dup);
  135. auto node5 = Node([node1]);
  136. auto node6 = Node([node1, node2, node3, node4]);
  137. assert(!anchorable(node1));
  138. assert(!anchorable(node2));
  139. assert(anchorable(node3));
  140. assert(anchorable(node4));
  141. assert(!anchorable(node5));
  142. assert(anchorable(node6));
  143. }
  144.  
  145. ///Add an anchor to the node if it's anchor-able and not anchored yet.
  146. void anchorNode(ref Node node) @safe
  147. {
  148. if(!anchorable(node)){return;}
  149.  
  150. if((node in anchors_) !is null)
  151. {
  152. if(anchors_[node] is null)
  153. {
  154. anchors_[node] = generateAnchor();
  155. }
  156. return;
  157. }
  158.  
  159. anchors_.remove(node);
  160. final switch (node.nodeID)
  161. {
  162. case NodeID.mapping:
  163. foreach(ref Node key, ref Node value; node)
  164. {
  165. anchorNode(key);
  166. anchorNode(value);
  167. }
  168. break;
  169. case NodeID.sequence:
  170. foreach(ref Node item; node)
  171. {
  172. anchorNode(item);
  173. }
  174. break;
  175. case NodeID.invalid:
  176. assert(0);
  177. case NodeID.scalar:
  178. }
  179. }
  180.  
  181. ///Generate and return a new anchor.
  182. string generateAnchor() @safe
  183. {
  184. ++lastAnchorID_;
  185. auto appender = appender!string();
  186. formattedWrite(appender, "id%03d", lastAnchorID_);
  187. return appender.data;
  188. }
  189.  
  190. ///Serialize a node and all its sub-nodes.
  191. void serializeNode(EmitterT)(ref EmitterT emitter, ref Node node) @safe
  192. {
  193. //If the node has an anchor, emit an anchor (as aliasEvent) on the
  194. //first occurrence, save it in serializedNodes_, and emit an alias
  195. //if it reappears.
  196. string aliased;
  197. if(anchorable(node) && (node in anchors_) !is null)
  198. {
  199. aliased = anchors_[node];
  200. if((node in serializedNodes_) !is null)
  201. {
  202. emitter.emit(aliasEvent(Mark(), Mark(), aliased));
  203. return;
  204. }
  205. serializedNodes_[node] = true;
  206. }
  207. final switch (node.nodeID)
  208. {
  209. case NodeID.mapping:
  210. const defaultTag = resolver_.defaultMappingTag;
  211. const implicit = node.tag_ == defaultTag;
  212. emitter.emit(mappingStartEvent(Mark(), Mark(), aliased, node.tag_,
  213. implicit, node.collectionStyle));
  214. foreach(ref Node key, ref Node value; node)
  215. {
  216. serializeNode(emitter, key);
  217. serializeNode(emitter, value);
  218. }
  219. emitter.emit(mappingEndEvent(Mark(), Mark()));
  220. return;
  221. case NodeID.sequence:
  222. const defaultTag = resolver_.defaultSequenceTag;
  223. const implicit = node.tag_ == defaultTag;
  224. emitter.emit(sequenceStartEvent(Mark(), Mark(), aliased, node.tag_,
  225. implicit, node.collectionStyle));
  226. foreach(ref Node item; node)
  227. {
  228. serializeNode(emitter, item);
  229. }
  230. emitter.emit(sequenceEndEvent(Mark(), Mark()));
  231. return;
  232. case NodeID.scalar:
  233. assert(node.type == NodeType.string, "Scalar node type must be string before serialized");
  234. auto value = node.as!string;
  235. const detectedTag = resolver_.resolve(NodeID.scalar, null, value, true);
  236. const bool isDetected = node.tag_ == detectedTag;
  237.  
  238. emitter.emit(scalarEvent(Mark(), Mark(), aliased, node.tag_,
  239. isDetected, value.idup, node.scalarStyle));
  240. return;
  241. case NodeID.invalid:
  242. assert(0);
  243. }
  244. }
  245. }
  246.  
  247. // Issue #244
  248. @safe unittest
  249. {
  250. import dub.internal.dyaml.dumper : dumper;
  251. auto node = Node([
  252. Node.Pair(
  253. Node(""),
  254. Node([
  255. Node([
  256. Node.Pair(
  257. Node("d"),
  258. Node([
  259. Node([
  260. Node.Pair(
  261. Node("c"),
  262. Node("")
  263. ),
  264. Node.Pair(
  265. Node("b"),
  266. Node("")
  267. ),
  268. Node.Pair(
  269. Node(""),
  270. Node("")
  271. )
  272. ])
  273. ])
  274. ),
  275. ]),
  276. Node([
  277. Node.Pair(
  278. Node("d"),
  279. Node([
  280. Node(""),
  281. Node(""),
  282. Node([
  283. Node.Pair(
  284. Node("c"),
  285. Node("")
  286. ),
  287. Node.Pair(
  288. Node("b"),
  289. Node("")
  290. ),
  291. Node.Pair(
  292. Node(""),
  293. Node("")
  294. )
  295. ])
  296. ])
  297. ),
  298. Node.Pair(
  299. Node("z"),
  300. Node("")
  301. ),
  302. Node.Pair(
  303. Node(""),
  304. Node("")
  305. )
  306. ]),
  307. Node("")
  308. ])
  309. ),
  310. Node.Pair(
  311. Node("g"),
  312. Node("")
  313. ),
  314. Node.Pair(
  315. Node("h"),
  316. Node("")
  317. ),
  318. ]);
  319.  
  320. auto stream = appender!string();
  321. dumper().dump(stream, node);
  322. }