aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ext_depends/D-YAML/source/dyaml/loader.d
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext_depends/D-YAML/source/dyaml/loader.d')
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/loader.d394
1 files changed, 394 insertions, 0 deletions
diff --git a/src/ext_depends/D-YAML/source/dyaml/loader.d b/src/ext_depends/D-YAML/source/dyaml/loader.d
new file mode 100644
index 0000000..7e7096c
--- /dev/null
+++ b/src/ext_depends/D-YAML/source/dyaml/loader.d
@@ -0,0 +1,394 @@
+
+// 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)
+
+/// Class used to load YAML documents.
+module dyaml.loader;
+
+
+import std.exception;
+import std.file;
+import std.stdio : File;
+import std.string;
+
+import dyaml.composer;
+import dyaml.constructor;
+import dyaml.event;
+import dyaml.exception;
+import dyaml.node;
+import dyaml.parser;
+import dyaml.reader;
+import dyaml.resolver;
+import dyaml.scanner;
+import dyaml.token;
+
+
+/** Loads YAML documents from files or char[].
+ *
+ * User specified Constructor and/or Resolver can be used to support new
+ * tags / data types.
+ */
+struct Loader
+{
+ private:
+ // Processes character data to YAML tokens.
+ Scanner scanner_;
+ // Processes tokens to YAML events.
+ Parser parser_;
+ // Resolves tags (data types).
+ Resolver resolver_;
+ // Name of the input file or stream, used in error messages.
+ string name_ = "<unknown>";
+ // Are we done loading?
+ bool done_;
+ // Last node read from stream
+ Node currentNode;
+ // Has the range interface been initialized yet?
+ bool rangeInitialized;
+
+ public:
+ @disable this();
+ @disable int opCmp(ref Loader);
+ @disable bool opEquals(ref Loader);
+
+ /** Construct a Loader to load YAML from a file.
+ *
+ * Params: filename = Name of the file to load from.
+ * file = Already-opened file to load from.
+ *
+ * Throws: YAMLException if the file could not be opened or read.
+ */
+ static Loader fromFile(string filename) @trusted
+ {
+ try
+ {
+ auto loader = Loader(std.file.read(filename), filename);
+ return loader;
+ }
+ catch(FileException e)
+ {
+ throw new YAMLException("Unable to open file %s for YAML loading: %s"
+ .format(filename, e.msg), e.file, e.line);
+ }
+ }
+ /// ditto
+ static Loader fromFile(File file) @system
+ {
+ auto loader = Loader(file.byChunk(4096).join, file.name);
+ return loader;
+ }
+
+ /** Construct a Loader to load YAML from a string.
+ *
+ * Params: data = String to load YAML from. The char[] version $(B will)
+ * overwrite its input during parsing as D:YAML reuses memory.
+ *
+ * Returns: Loader loading YAML from given string.
+ *
+ * Throws:
+ *
+ * YAMLException if data could not be read (e.g. a decoding error)
+ */
+ static Loader fromString(char[] data) @safe
+ {
+ return Loader(cast(ubyte[])data);
+ }
+ /// Ditto
+ static Loader fromString(string data) @safe
+ {
+ return fromString(data.dup);
+ }
+ /// Load a char[].
+ @safe unittest
+ {
+ assert(Loader.fromString("42".dup).load().as!int == 42);
+ }
+ /// Load a string.
+ @safe unittest
+ {
+ assert(Loader.fromString("42").load().as!int == 42);
+ }
+
+ /** Construct a Loader to load YAML from a buffer.
+ *
+ * Params: yamlData = Buffer with YAML data to load. This may be e.g. a file
+ * loaded to memory or a string with YAML data. Note that
+ * buffer $(B will) be overwritten, as D:YAML minimizes
+ * memory allocations by reusing the input _buffer.
+ * $(B Must not be deleted or modified by the user as long
+ * as nodes loaded by this Loader are in use!) - Nodes may
+ * refer to data in this buffer.
+ *
+ * Note that D:YAML looks for byte-order-marks YAML files encoded in
+ * UTF-16/UTF-32 (and sometimes UTF-8) use to specify the encoding and
+ * endianness, so it should be enough to load an entire file to a buffer and
+ * pass it to D:YAML, regardless of Unicode encoding.
+ *
+ * Throws: YAMLException if yamlData contains data illegal in YAML.
+ */
+ static Loader fromBuffer(ubyte[] yamlData) @safe
+ {
+ return Loader(yamlData);
+ }
+ /// Ditto
+ static Loader fromBuffer(void[] yamlData) @system
+ {
+ return Loader(yamlData);
+ }
+ /// Ditto
+ private this(void[] yamlData, string name = "<unknown>") @system
+ {
+ this(cast(ubyte[])yamlData, name);
+ }
+ /// Ditto
+ private this(ubyte[] yamlData, string name = "<unknown>") @safe
+ {
+ resolver_ = Resolver.withDefaultResolvers;
+ name_ = name;
+ try
+ {
+ auto reader_ = new Reader(yamlData, name);
+ scanner_ = Scanner(reader_);
+ parser_ = new Parser(scanner_);
+ }
+ catch(YAMLException e)
+ {
+ throw new YAMLException("Unable to open %s for YAML loading: %s"
+ .format(name_, e.msg), e.file, e.line);
+ }
+ }
+
+
+ /// Set stream _name. Used in debugging messages.
+ void name(string name) pure @safe nothrow @nogc
+ {
+ name_ = name;
+ }
+
+ /// Specify custom Resolver to use.
+ auto ref resolver() pure @safe nothrow @nogc
+ {
+ return resolver_;
+ }
+
+ /** Load single YAML document.
+ *
+ * If none or more than one YAML document is found, this throws a YAMLException.
+ *
+ * This can only be called once; this is enforced by contract.
+ *
+ * Returns: Root node of the document.
+ *
+ * Throws: YAMLException if there wasn't exactly one document
+ * or on a YAML parsing error.
+ */
+ Node load() @safe
+ {
+ enforce!YAMLException(!empty, "Zero documents in stream");
+ auto output = front;
+ popFront();
+ enforce!YAMLException(empty, "More than one document in stream");
+ return output;
+ }
+
+ /** Implements the empty range primitive.
+ *
+ * If there's no more documents left in the stream, this will be true.
+ *
+ * Returns: `true` if no more documents left, `false` otherwise.
+ */
+ bool empty() @safe
+ {
+ // currentNode and done_ are both invalid until popFront is called once
+ if (!rangeInitialized)
+ {
+ popFront();
+ }
+ return done_;
+ }
+ /** Implements the popFront range primitive.
+ *
+ * Reads the next document from the stream, if possible.
+ */
+ void popFront() @safe
+ {
+ // Composer initialization is done here in case the constructor is
+ // modified, which is a pretty common case.
+ static Composer composer;
+ if (!rangeInitialized)
+ {
+ composer = Composer(parser_, resolver_);
+ rangeInitialized = true;
+ }
+ assert(!done_, "Loader.popFront called on empty range");
+ if (composer.checkNode())
+ {
+ currentNode = composer.getNode();
+ }
+ else
+ {
+ done_ = true;
+ }
+ }
+ /** Implements the front range primitive.
+ *
+ * Returns: the current document as a Node.
+ */
+ Node front() @safe
+ {
+ // currentNode and done_ are both invalid until popFront is called once
+ if (!rangeInitialized)
+ {
+ popFront();
+ }
+ return currentNode;
+ }
+
+ // Scan all tokens, throwing them away. Used for benchmarking.
+ void scanBench() @safe
+ {
+ try
+ {
+ while(!scanner_.empty)
+ {
+ scanner_.popFront();
+ }
+ }
+ catch(YAMLException e)
+ {
+ throw new YAMLException("Unable to scan YAML from stream " ~
+ name_ ~ " : " ~ e.msg, e.file, e.line);
+ }
+ }
+
+
+ // Parse and return all events. Used for debugging.
+ auto parse() @safe
+ {
+ return parser_;
+ }
+}
+/// Load single YAML document from a file:
+@safe unittest
+{
+ write("example.yaml", "Hello world!");
+ auto rootNode = Loader.fromFile("example.yaml").load();
+ assert(rootNode == "Hello world!");
+}
+/// Load single YAML document from an already-opened file:
+@system unittest
+{
+ // Open a temporary file
+ auto file = File.tmpfile;
+ // Write valid YAML
+ file.write("Hello world!");
+ // Return to the beginning
+ file.seek(0);
+ // Load document
+ auto rootNode = Loader.fromFile(file).load();
+ assert(rootNode == "Hello world!");
+}
+/// Load all YAML documents from a file:
+@safe unittest
+{
+ import std.array : array;
+ import std.file : write;
+ write("example.yaml",
+ "---\n"~
+ "Hello world!\n"~
+ "...\n"~
+ "---\n"~
+ "Hello world 2!\n"~
+ "...\n"
+ );
+ auto nodes = Loader.fromFile("example.yaml").array;
+ assert(nodes.length == 2);
+}
+/// Iterate over YAML documents in a file, lazily loading them:
+@safe unittest
+{
+ import std.file : write;
+ write("example.yaml",
+ "---\n"~
+ "Hello world!\n"~
+ "...\n"~
+ "---\n"~
+ "Hello world 2!\n"~
+ "...\n"
+ );
+ auto loader = Loader.fromFile("example.yaml");
+
+ foreach(ref node; loader)
+ {
+ //Do something
+ }
+}
+/// Load YAML from a string:
+@safe unittest
+{
+ string yaml_input = ("red: '#ff0000'\n" ~
+ "green: '#00ff00'\n" ~
+ "blue: '#0000ff'");
+
+ auto colors = Loader.fromString(yaml_input).load();
+
+ foreach(string color, string value; colors)
+ {
+ // Do something with the color and its value...
+ }
+}
+
+/// Load a file into a buffer in memory and then load YAML from that buffer:
+@safe unittest
+{
+ import std.file : read, write;
+ import std.stdio : writeln;
+ // Create a yaml document
+ write("example.yaml",
+ "---\n"~
+ "Hello world!\n"~
+ "...\n"~
+ "---\n"~
+ "Hello world 2!\n"~
+ "...\n"
+ );
+ try
+ {
+ string buffer = readText("example.yaml");
+ auto yamlNode = Loader.fromString(buffer);
+
+ // Read data from yamlNode here...
+ }
+ catch(FileException e)
+ {
+ writeln("Failed to read file 'example.yaml'");
+ }
+}
+/// Use a custom resolver to support custom data types and/or implicit tags:
+@safe unittest
+{
+ import std.file : write;
+ // Create a yaml document
+ write("example.yaml",
+ "---\n"~
+ "Hello world!\n"~
+ "...\n"
+ );
+
+ auto loader = Loader.fromFile("example.yaml");
+
+ // Add resolver expressions here...
+ // loader.resolver.addImplicitResolver(...);
+
+ auto rootNode = loader.load();
+}
+
+//Issue #258 - https://github.com/dlang-community/D-YAML/issues/258
+@safe unittest
+{
+ auto yaml = "{\n\"root\": {\n\t\"key\": \"value\"\n }\n}";
+ auto doc = Loader.fromString(yaml).load();
+ assert(doc.isValid);
+}