diff --git a/.gitignore b/.gitignore index eea23d9..29b24a5 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,6 @@ Thumbs.db perf.data perf.data.old callgrind.out.* + +# Test outputs # +example.yaml diff --git a/source/dyaml/constructor.d b/source/dyaml/constructor.d index a6b7882..9c14604 100644 --- a/source/dyaml/constructor.d +++ b/source/dyaml/constructor.d @@ -137,47 +137,6 @@ final class Constructor * * Params: tag = Tag for the function to handle. * ctor = Constructor function. - * - * Example: - * - * -------------------- - * import std.string; - * - * import dyaml.all; - * - * struct MyStruct - * { - * int x, y, z; - * - * //Any D:YAML type must have a custom opCmp operator. - * //This is used for ordering in mappings. - * const int opCmp(ref const MyStruct s) - * { - * if(x != s.x){return x - s.x;} - * if(y != s.y){return y - s.y;} - * if(z != s.z){return z - s.z;} - * return 0; - * } - * } - * - * MyStruct constructMyStructScalar(ref Node node) - * { - * //Guaranteed to be string as we construct from scalar. - * //!mystruct x:y:z - * auto parts = node.as!string().split(":"); - * // If this throws, the D:YAML will handle it and throw a YAMLException. - * return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2])); - * } - * - * void main() - * { - * auto loader = Loader("file.yaml"); - * auto constructor = new Constructor; - * constructor.addConstructorScalar("!mystruct", &constructMyStructScalar); - * loader.constructor = constructor; - * Node node = loader.load(); - * } - * -------------------- */ void addConstructorScalar(T)(const string tag, T function(ref Node) ctor) { @@ -185,49 +144,44 @@ final class Constructor auto deleg = addConstructor!T(t, ctor); (*delegates!string)[t] = deleg; } + /// + @safe unittest + { + static struct MyStruct + { + int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } + } + + static MyStruct constructMyStructScalar(ref Node node) @safe + { + //Guaranteed to be string as we construct from scalar. + //!mystruct x:y:z + auto parts = node.as!string().split(":"); + // If this throws, the D:YAML will handle it and throw a YAMLException. + return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2])); + } + + auto loader = Loader.fromString("!mystruct 12:34:56".dup); + auto constructor = new Constructor; + constructor.addConstructorScalar("!mystruct", &constructMyStructScalar); + loader.constructor = constructor; + Node node = loader.load(); + assert(node.get!MyStruct == MyStruct(12, 34, 56)); + } /** Add a constructor function from sequence. * * See_Also: addConstructorScalar - * - * Example: - * - * -------------------- - * import std.string; - * - * import dyaml.all; - * - * struct MyStruct - * { - * int x, y, z; - * - * //Any D:YAML type must have a custom opCmp operator. - * //This is used for ordering in mappings. - * const int opCmp(ref const MyStruct s) - * { - * if(x != s.x){return x - s.x;} - * if(y != s.y){return y - s.y;} - * if(z != s.z){return z - s.z;} - * return 0; - * } - * } - * - * MyStruct constructMyStructSequence(ref Node node) - * { - * //node is guaranteed to be sequence. - * //!mystruct [x, y, z] - * return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int); - * } - * - * void main() - * { - * auto loader = Loader("file.yaml"); - * auto constructor = new Constructor; - * constructor.addConstructorSequence("!mystruct", &constructMyStructSequence); - * loader.constructor = constructor; - * Node node = loader.load(); - * } - * -------------------- */ void addConstructorSequence(T)(const string tag, T function(ref Node) ctor) { @@ -235,49 +189,40 @@ final class Constructor auto deleg = addConstructor!T(t, ctor); (*delegates!(Node[]))[t] = deleg; } + /// + @safe unittest { + static struct MyStruct + { + int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } + } + + static MyStruct constructMyStructSequence(ref Node node) @safe + { + //node is guaranteed to be sequence. + //!mystruct [x, y, z] + return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int); + } + auto loader = Loader.fromString("!mystruct [1,2,3]".dup); + auto constructor = new Constructor; + constructor.addConstructorSequence("!mystruct", &constructMyStructSequence); + loader.constructor = constructor; + Node node = loader.load(); + assert(node.get!MyStruct == MyStruct(1, 2, 3)); + } /** Add a constructor function from a mapping. * * See_Also: addConstructorScalar - * - * Example: - * - * -------------------- - * import std.string; - * - * import dyaml.all; - * - * struct MyStruct - * { - * int x, y, z; - * - * //Any D:YAML type must have a custom opCmp operator. - * //This is used for ordering in mappings. - * const int opCmp(ref const MyStruct s) - * { - * if(x != s.x){return x - s.x;} - * if(y != s.y){return y - s.y;} - * if(z != s.z){return z - s.z;} - * return 0; - * } - * } - * - * MyStruct constructMyStructMapping(ref Node node) - * { - * //node is guaranteed to be mapping. - * //!mystruct {"x": x, "y": y, "z": z} - * return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int); - * } - * - * void main() - * { - * auto loader = Loader("file.yaml"); - * auto constructor = new Constructor; - * constructor.addConstructorMapping("!mystruct", &constructMyStructMapping); - * loader.constructor = constructor; - * Node node = loader.load(); - * } - * -------------------- */ void addConstructorMapping(T)(const string tag, T function(ref Node) ctor) { @@ -285,6 +230,36 @@ final class Constructor auto deleg = addConstructor!T(t, ctor); (*delegates!(Node.Pair[]))[t] = deleg; } + /// + @safe unittest { + static struct MyStruct + { + int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } + } + + static MyStruct constructMyStructMapping(ref Node node) @safe + { + //node is guaranteed to be mapping. + //!mystruct {"x": x, "y": y, "z": z} + return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int); + } + auto loader = Loader.fromString(`!mystruct {"x": 11, "y": 22, "z": 33}`.dup); + auto constructor = new Constructor; + constructor.addConstructorMapping("!mystruct", &constructMyStructMapping); + loader.constructor = constructor; + Node node = loader.load(); + assert(node.get!MyStruct == MyStruct(11, 22, 33)); + } package: /* diff --git a/source/dyaml/dumper.d b/source/dyaml/dumper.d index 752f885..63307e2 100644 --- a/source/dyaml/dumper.d +++ b/source/dyaml/dumper.d @@ -31,52 +31,10 @@ import dyaml.tagdirective; /** * Dumps YAML documents to files or streams. * - * 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. * * Setters are provided to affect output details (style, encoding, etc.). - * - * Examples: - * - * Write to a file: - * -------------------- - * auto node = Node([1, 2, 3, 4, 5]); - * Dumper("file.yaml").dump(node); - * -------------------- - * - * Write multiple YAML documents to a file: - * -------------------- - * auto node1 = Node([1, 2, 3, 4, 5]); - * auto node2 = Node("This document contains only one string"); - * Dumper("file.yaml").dump(node1, node2); - * - * //Or with an array: - * //Dumper("file.yaml").dump([node1, node2]); - * - * - * -------------------- - * - * Write to memory: - * -------------------- - * import std.stream; - * auto stream = new YMemoryStream(); - * auto node = Node([1, 2, 3, 4, 5]); - * Dumper(stream).dump(node); - * -------------------- - * - * Use a custom representer/resolver to support custom data types and/or implicit tags: - * -------------------- - * auto node = Node([1, 2, 3, 4, 5]); - * auto representer = new Representer(); - * auto resolver = new Resolver(); - * - * //Add representer functions / resolver expressions here... - * - * auto dumper = Dumper("file.yaml"); - * dumper.representer = representer; - * dumper.resolver = resolver; - * dumper.dump(node); - * -------------------- */ struct Dumper { @@ -271,21 +229,10 @@ struct Dumper * * Each prefix MUST not be empty. * - * The "!!" handle is used for default YAML _tags with prefix + * The "!!" handle is used for default YAML _tags with prefix * "tag:yaml.org,2002:". This can be overridden. * * Params: tags = Tag directives (keys are handles, values are prefixes). - * - * Example: - * -------------------- - * Dumper dumper = Dumper("file.yaml"); - * string[string] directives; - * directives["!short!"] = "tag:long.org,2011:"; - * //This will emit tags starting with "tag:long.org,2011" - * //with a "!short!" prefix instead. - * dumper.tagDirectives(directives); - * dumper.dump(Node("foo")); - * -------------------- */ @property void tagDirectives(string[string] tags) pure @safe { @@ -300,6 +247,17 @@ struct Dumper } tags_ = t; } + /// + @safe unittest + { + Dumper dumper = Dumper("example.yaml"); + string[string] directives; + directives["!short!"] = "tag:long.org,2011:"; + //This will emit tags starting with "tag:long.org,2011" + //with a "!short!" prefix instead. + dumper.tagDirectives(directives); + dumper.dump(Node("foo")); + } /** * Dump one or more YAML _documents to the file/stream. @@ -357,3 +315,38 @@ struct Dumper } } } +///Write to a file +@safe unittest +{ + auto node = Node([1, 2, 3, 4, 5]); + Dumper("example.yaml").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); + //Or with an array: + Dumper("example.yaml").dump([node1, node2]); +} +///Write to memory +@safe unittest +{ + import dyaml.stream; + auto stream = new YMemoryStream(); + auto node = Node([1, 2, 3, 4, 5]); + Dumper(stream).dump(node); +} +///Use a custom representer/resolver to support custom data types and/or implicit tags +@safe unittest +{ + auto node = Node([1, 2, 3, 4, 5]); + auto representer = new Representer(); + auto resolver = new Resolver(); + //Add representer functions / resolver expressions here... + auto dumper = Dumper("example.yaml"); + dumper.representer = representer; + dumper.resolver = resolver; + dumper.dump(node); +} diff --git a/source/dyaml/event.d b/source/dyaml/event.d index 7be2443..f80d88f 100644 --- a/source/dyaml/event.d +++ b/source/dyaml/event.d @@ -123,6 +123,7 @@ Event event(EventID id)(const Mark start, const Mark end, const string anchor = * anchor = Anchor of the sequence, if any. * tag = Tag of the sequence, if specified. * implicit = Should the tag be implicitly resolved? + * style = Style to use when outputting document. */ Event collectionStartEvent(EventID id) (const Mark start, const Mark end, const string anchor, const string tag, diff --git a/source/dyaml/flags.d b/source/dyaml/flags.d index 9287360..d5db3c9 100644 --- a/source/dyaml/flags.d +++ b/source/dyaml/flags.d @@ -17,20 +17,6 @@ package: * Struct holding multiple named boolean values in a single byte. * * Can hold at most 8 values. - * - * Example: - * -------------------- - * Flags!("empty", "multiline") flags; - * assert(flags.empty == false && flags.multiline == false); - * flags.multiline = true; - * assert(flags.empty == false && flags.multiline == true); - * flags.empty = true; - * assert(flags.empty == true && flags.multiline == true); - * flags.multiline = false; - * assert(flags.empty == true && flags.multiline == false); - * flags.empty = false; - * assert(flags.empty == false && flags.multiline == false); - * -------------------- */ struct Flags(names ...) if(names.length <= 8) { @@ -72,6 +58,7 @@ struct Flags(names ...) if(names.length <= 8) ///Flag accessors. mixin(flags(names)); } +/// @safe unittest { import std.stdio; diff --git a/source/dyaml/hacks.d b/source/dyaml/hacks.d index 8f4a539..d69e985 100644 --- a/source/dyaml/hacks.d +++ b/source/dyaml/hacks.d @@ -23,22 +23,22 @@ import dyaml.style; * However, determining style may be useful in some cases, e.g. YAML utilities. * * May only be called on scalar nodes (nodes where node.isScalar() == true). - * - * Example: - * -------------------- - * // Node node // loaded from a file - * if(node.isScalar) - * { - * import std.stdio; - * writeln(node.scalarStyleHack()); - * } - * -------------------- */ ScalarStyle scalarStyleHack(ref const(Node) node) @safe nothrow { assert(node.isScalar, "Trying to get scalar style of a non-scalar node"); return node.scalarStyle; } +/// +@safe unittest +{ + import dyaml; + Node node = Loader.fromString(`"42"`.dup).load(); // loaded from a file + if(node.isScalar) + { + assert(node.scalarStyleHack() == ScalarStyle.DoubleQuoted); + } +} @safe unittest { writeln("D:YAML scalarStyleHack getter unittest"); diff --git a/source/dyaml/loader.d b/source/dyaml/loader.d index 1a181a8..697d3ff 100644 --- a/source/dyaml/loader.d +++ b/source/dyaml/loader.d @@ -28,74 +28,6 @@ import dyaml.token; * * User specified Constructor and/or Resolver can be used to support new * tags / data types. - * - * Examples: - * - * Load single YAML document from a file: - * -------------------- - * auto rootNode = Loader("file.yaml").load(); - * ... - * -------------------- - * - * Load all YAML documents from a file: - * -------------------- - * auto nodes = Loader("file.yaml").loadAll(); - * ... - * -------------------- - * - * Iterate over YAML documents in a file, lazily loading them: - * -------------------- - * auto loader = Loader("file.yaml"); - * - * foreach(ref node; loader) - * { - * ... - * } - * -------------------- - * - * Load YAML from a string: - * -------------------- - * char[] yaml_input = "red: '#ff0000'\n" - * "green: '#00ff00'\n" - * "blue: '#0000ff'".dup; - * - * auto colors = Loader.fromString(yaml_input).load(); - * - * foreach(string color, string value; colors) - * { - * import std.stdio; - * writeln(color, " is ", value, " in HTML/CSS"); - * } - * -------------------- - * - * Load a file into a buffer in memory and then load YAML from that buffer: - * -------------------- - * try - * { - * import std.file; - * void[] buffer = std.file.read("file.yaml"); - * auto yamlNode = Loader(buffer); - * - * // Read data from yamlNode here... - * } - * catch(FileException e) - * { - * writeln("Failed to read file 'file.yaml'"); - * } - * -------------------- - * - * Use a custom constructor/resolver to support custom data types and/or implicit tags: - * -------------------- - * auto constructor = new Constructor(); - * auto resolver = new Resolver(); - * - * // Add constructor functions / resolver expressions here... - * - * auto loader = Loader("file.yaml"); - * loader.constructor = constructor; - * loader.resolver = resolver; - * auto rootNode = loader.load(node); - * -------------------- */ struct Loader { @@ -375,7 +307,48 @@ struct Loader if(constructor_ is null) { constructor_ = new Constructor(); } } } +/// Load single YAML document from a file: +@safe unittest +{ + write("example.yaml", "Hello world!"); + auto rootNode = Loader("example.yaml").load(); + assert(rootNode == "Hello world!"); +} +/// Load all YAML documents from a file: +@safe unittest +{ + import std.file : write; + write("example.yaml", + "---\n"~ + "Hello world!\n"~ + "...\n"~ + "---\n"~ + "Hello world 2!\n"~ + "...\n" + ); + auto nodes = Loader("example.yaml").loadAll(); + 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("example.yaml"); + foreach(ref node; loader) + { + //Do something + } +} +/// Load YAML from a string: @safe unittest { char[] yaml_input = ("red: '#ff0000'\n" ~ @@ -390,3 +363,50 @@ struct Loader writeln(color, " is ", value, " in HTML/CSS"); } } + +/// 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 + { + void[] buffer = read("example.yaml"); + auto yamlNode = Loader(buffer); + + // Read data from yamlNode here... + } + catch(FileException e) + { + writeln("Failed to read file 'example.yaml'"); + } +} +/// Use a custom constructor/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 constructor = new Constructor(); + auto resolver = new Resolver(); + + // Add constructor functions / resolver expressions here... + + auto loader = Loader("example.yaml"); + loader.constructor = constructor; + loader.resolver = resolver; + auto rootNode = loader.load(); +} diff --git a/source/dyaml/node.d b/source/dyaml/node.d index 3732299..068787c 100644 --- a/source/dyaml/node.d +++ b/source/dyaml/node.d @@ -311,13 +311,6 @@ struct Node * must be in full form, e.g. "tag:yaml.org,2002:set", * not a shortcut, like "!!set". * - * Examples: - * -------------------- - * // Will be emitted as a sequence (default for arrays) - * auto seq = Node([1, 2, 3, 4, 5]); - * // Will be emitted as a set (overriden tag) - * auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set"); - * -------------------- */ this(T)(T[] array, const string tag = null) @trusted if (!isSomeString!(T[])) @@ -341,6 +334,14 @@ struct Node value_ = Value(nodes); } } + /// + @safe unittest + { + // Will be emitted as a sequence (default for arrays) + auto seq = Node([1, 2, 3, 4, 5]); + // Will be emitted as a set (overriden tag) + auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set"); + } @safe unittest { with(Node([1, 2, 3])) @@ -350,10 +351,6 @@ struct Node assert(opIndex(2).as!int == 3); } - // Will be emitted as a sequence (default for arrays) - auto seq = Node([1, 2, 3, 4, 5]); - // Will be emitted as a set (overriden tag) - auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set"); } /** Construct a node from an associative _array. @@ -373,15 +370,6 @@ struct Node * in full form, e.g. "tag:yaml.org,2002:omap", not a * shortcut, like "!!omap". * - * Examples: - * -------------------- - * // Will be emitted as an unordered mapping (default for mappings) - * auto map = Node([1 : "a", 2 : "b"]); - * // Will be emitted as an ordered map (overriden tag) - * auto omap = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:omap"); - * // Will be emitted as pairs (overriden tag) - * auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs"); - * -------------------- */ this(K, V)(V[K] array, const string tag = null) @trusted { @@ -391,6 +379,16 @@ struct Node foreach(key, ref value; array){pairs ~= Pair(key, value);} value_ = Value(pairs); } + /// + @safe unittest + { + // Will be emitted as an unordered mapping (default for mappings) + auto map = Node([1 : "a", 2 : "b"]); + // Will be emitted as an ordered map (overriden tag) + auto omap = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:omap"); + // Will be emitted as pairs (overriden tag) + auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs"); + } @safe unittest { int[string] aa; @@ -403,12 +401,6 @@ struct Node assert(opIndex("2").as!int == 2); } - // Will be emitted as an unordered mapping (default for mappings) - auto map = Node([1 : "a", 2 : "b"]); - // Will be emitted as an ordered map (overriden tag) - auto omap = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:omap"); - // Will be emitted as pairs (overriden tag) - auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs"); } /** Construct a node from arrays of _keys and _values. @@ -437,15 +429,6 @@ struct Node * in full form, e.g. "tag:yaml.org,2002:omap", not a * shortcut, like "!!omap". * - * Examples: - * -------------------- - * // Will be emitted as an unordered mapping (default for mappings) - * auto map = Node([1, 2], ["a", "b"]); - * // Will be emitted as an ordered map (overriden tag) - * auto omap = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:omap"); - * // Will be emitted as pairs (overriden tag) - * auto pairs = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:pairs"); - * -------------------- */ this(K, V)(K[] keys, V[] values, const string tag = null) @trusted if(!(isSomeString!(K[]) || isSomeString!(V[]))) @@ -463,6 +446,16 @@ struct Node foreach(i; 0 .. keys.length){pairs ~= Pair(keys[i], values[i]);} value_ = Value(pairs); } + /// + @safe unittest + { + // Will be emitted as an unordered mapping (default for mappings) + auto map = Node([1, 2], ["a", "b"]); + // Will be emitted as an ordered map (overridden tag) + auto omap = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:omap"); + // Will be emitted as pairs (overriden tag) + auto pairs = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:pairs"); + } @safe unittest { with(Node(["1", "2"], [1, 2])) @@ -472,12 +465,6 @@ struct Node assert(opIndex("2").as!int == 2); } - // Will be emitted as an unordered mapping (default for mappings) - auto map = Node([1, 2], ["a", "b"]); - // Will be emitted as an ordered map (overriden tag) - auto omap = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:omap"); - // Will be emitted as pairs (overriden tag) - auto pairs = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:pairs"); } /// Is this node valid (initialized)? @@ -580,17 +567,6 @@ struct Node * by old versions of the program, which expect the node to be a scalar. * ) * - * Examples: - * - * Automatic type conversion: - * -------------------- - * auto node = Node(42); - * - * assert(node.as!int == 42); - * assert(node.as!string == "42"); - * assert(node.as!double == 42.0); - * -------------------- - * * Returns: Value of the node as specified type. * * Throws: NodeException if unable to convert to specified type, or if @@ -669,6 +645,15 @@ struct Node } assert(false, "This code should never be reached"); } + /// Automatic type conversion + @safe unittest + { + auto node = Node(42); + + assert(node.get!int == 42); + assert(node.get!string == "42"); + assert(node.get!double == 42.0); + } @safe unittest { assertThrown!NodeException(Node("42").get!int); @@ -968,7 +953,9 @@ struct Node * * To set element at a null index, use YAMLNull for index. * - * Params: index = Index of the value to set. + * Params: + * value = Value to assign. + * index = Index of the value to set. * * Throws: NodeException if the node is not a collection, index is out * of range or if a non-integral index is used on a sequence node. diff --git a/source/dyaml/nogcutil.d b/source/dyaml/nogcutil.d index 85ae430..fe53ac8 100644 --- a/source/dyaml/nogcutil.d +++ b/source/dyaml/nogcutil.d @@ -363,17 +363,7 @@ auto decodeUTF8NoGC(Flag!"validated" validated)(const(char[]) str, ref size_t in else { return invalidUTF; } } -/// @nogc version of std.utf.decode() for char[], but assumes str is valid UTF-8. -/// -/// The caller $(B must) handle ASCII (< 0x80) characters manually; this is asserted to -/// force code using this function to be efficient. -/// -/// Params: -/// -/// str = Will decode the first code point from this string. Must be valid UTF-8, -/// otherwise undefined behavior WILL occur. -/// index = Index in str where the code point starts. Will be updated to point to the -/// next code point. +/// ditto alias decodeValidUTF8NoGC = decodeUTF8NoGC!(Yes.validated); /// @nogc version of std.utf.encode() for char[]. @@ -451,18 +441,7 @@ auto encodeCharNoGC(Flag!"validated" validated)(ref char[4] buf, dchar c) } } -/// @nogc version of std.utf.encode() for char[], but assumes c is a valid UTF-32 char. -/// -/// The caller $(B must) handle ASCII (< 0x80) characters manually; this is asserted to -/// force code using this function to be efficient. -/// -/// Params: -/// -/// buf = Buffer to write the encoded result to. -/// c = Character to encode. Must be valid UTF-32, otherwise undefined behavior -/// $(D will) occur. -/// -/// Returns: Number of bytes the encoded character takes up in buf. +/// ditto alias encodeValidCharNoGC = encodeCharNoGC!(Yes.validated); /// @nogc version of std.utf.isValidDchar diff --git a/source/dyaml/representer.d b/source/dyaml/representer.d index 8e2c7d8..05a351d 100644 --- a/source/dyaml/representer.d +++ b/source/dyaml/representer.d @@ -108,7 +108,8 @@ final class Representer * throw a $(D RepresenterException). See the example for more information. * * - * Only one function may be specified for one data type. Default data + * + * Only one function may be specified for one data type. Default data * types already have representer functions unless disabled in the * $(D Representer) constructor. * @@ -121,106 +122,6 @@ final class Representer * values - it is not const for compatibility reasons. * * Params: representer = Representer function to add. - * - * Examples: - * - * Representing a simple struct: - * -------------------- - * import std.string; - * - * import dyaml.all; - * - * struct MyStruct - * { - * int x, y, z; - * - * //Any D:YAML type must have a custom opCmp operator. - * //This is used for ordering in mappings. - * const int opCmp(ref const MyStruct s) - * { - * if(x != s.x){return x - s.x;} - * if(y != s.y){return y - s.y;} - * if(z != s.z){return z - s.z;} - * return 0; - * } - * } - * - * Node representMyStruct(ref Node node, Representer representer) - * { - * //The node is guaranteed to be MyStruct as we add representer for MyStruct. - * auto value = node.as!MyStruct; - * //Using custom scalar format, x:y:z. - * auto scalar = format("%s:%s:%s", value.x, value.y, value.z); - * //Representing as a scalar, with custom tag to specify this data type. - * return representer.representScalar("!mystruct.tag", scalar); - * } - * - * void main() - * { - * auto dumper = Dumper("file.yaml"); - * auto representer = new Representer; - * representer.addRepresenter!MyStruct(&representMyStruct); - * dumper.representer = representer; - * dumper.dump(Node(MyStruct(1,2,3))); - * } - * -------------------- - * - * Representing a class: - * -------------------- - * import std.string; - * - * import dyaml.all; - * - * class MyClass - * { - * int x, y, z; - * - * this(int x, int y, int z) - * { - * this.x = x; - * this.y = y; - * this.z = z; - * } - * - * //Any D:YAML type must have a custom opCmp operator. - * //This is used for ordering in mappings. - * override int opCmp(Object o) - * { - * MyClass s = cast(MyClass)o; - * if(s is null){return -1;} - * if(x != s.x){return x - s.x;} - * if(y != s.y){return y - s.y;} - * if(z != s.z){return z - s.z;} - * return 0; - * } - * - * ///Useful for Node.as!string . - * override string toString() - * { - * return format("MyClass(%s, %s, %s)", x, y, z); - * } - * } - * - * //Same as representMyStruct. - * Node representMyClass(ref Node node, Representer representer) - * { - * //The node is guaranteed to be MyClass as we add representer for MyClass. - * auto value = node.as!MyClass; - * //Using custom scalar format, x:y:z. - * auto scalar = format("%s:%s:%s", value.x, value.y, value.z); - * //Representing as a scalar, with custom tag to specify this data type. - * return representer.representScalar("!myclass.tag", scalar); - * } - * - * void main() - * { - * auto dumper = Dumper("file.yaml"); - * auto representer = new Representer; - * representer.addRepresenter!MyClass(&representMyClass); - * dumper.representer = representer; - * dumper.dump(Node(new MyClass(1,2,3))); - * } - * -------------------- */ void addRepresenter(T)(Node function(ref Node, Representer) @safe representer) @safe pure @@ -230,6 +131,96 @@ final class Representer " already specified. Can't specify another one"); representers_[typeid(T)] = representer; } + /// Representing a simple struct: + unittest { + import std.string; + + import dyaml; + + struct MyStruct + { + int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } + } + + static Node representMyStruct(ref Node node, Representer representer) @safe + { + //The node is guaranteed to be MyStruct as we add representer for MyStruct. + auto value = node.as!MyStruct; + //Using custom scalar format, x:y:z. + auto scalar = format("%s:%s:%s", value.x, value.y, value.z); + //Representing as a scalar, with custom tag to specify this data type. + return representer.representScalar("!mystruct.tag", scalar); + } + + auto dumper = Dumper("example.yaml"); + auto representer = new Representer; + representer.addRepresenter!MyStruct(&representMyStruct); + dumper.representer = representer; + dumper.dump(Node(MyStruct(1,2,3))); + } + /// Representing a class: + unittest { + import std.string; + + import dyaml; + + class MyClass + { + int x, y, z; + + this(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + override int opCmp(Object o) + { + MyClass s = cast(MyClass)o; + if(s is null){return -1;} + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } + + ///Useful for Node.as!string . + override string toString() + { + return format("MyClass(%s, %s, %s)", x, y, z); + } + } + + //Same as representMyStruct. + static Node representMyClass(ref Node node, Representer representer) @safe + { + //The node is guaranteed to be MyClass as we add representer for MyClass. + auto value = node.as!MyClass; + //Using custom scalar format, x:y:z. + auto scalar = format("%s:%s:%s", value.x, value.y, value.z); + //Representing as a scalar, with custom tag to specify this data type. + return representer.representScalar("!myclass.tag", scalar); + } + + auto dumper = Dumper("example.yaml"); + auto representer = new Representer; + representer.addRepresenter!MyClass(&representMyClass); + dumper.representer = representer; + dumper.dump(Node(new MyClass(1,2,3))); + } //If profiling shows a bottleneck on tag construction in these 3 methods, //we'll need to take Tag directly and have string based wrappers for @@ -246,31 +237,6 @@ final class Representer * If the node was loaded before, previous _style will always be used. * * Returns: The represented node. - * - * Example: - * -------------------- - * struct MyStruct - * { - * int x, y, z; - * - * //Any D:YAML type must have a custom opCmp operator. - * //This is used for ordering in mappings. - * const int opCmp(ref const MyStruct s) - * { - * if(x != s.x){return x - s.x;} - * if(y != s.y){return y - s.y;} - * if(z != s.z){return z - s.z;} - * return 0; - * } - * } - * - * Node representMyStruct(ref Node node, Representer representer) - * { - * auto value = node.as!MyStruct; - * auto scalar = format("%s:%s:%s", value.x, value.y, value.z); - * return representer.representScalar("!mystruct.tag", scalar); - * } - * -------------------- */ Node representScalar(string tag, string scalar, ScalarStyle style = ScalarStyle.Invalid) @trusted @@ -279,6 +245,37 @@ final class Representer return Node.rawNode(Node.Value(scalar), Mark(), tag, style, CollectionStyle.Invalid); } + /// + @safe unittest + { + struct MyStruct + { + int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } + } + + static Node representMyStruct(ref Node node, Representer representer) + { + auto value = node.as!MyStruct; + auto scalar = format("%s:%s:%s", value.x, value.y, value.z); + return representer.representScalar("!mystruct.tag", scalar); + } + + auto dumper = Dumper("example.yaml"); + auto representer = new Representer; + representer.addRepresenter!MyStruct(&representMyStruct); + dumper.representer = representer; + dumper.dump(Node(MyStruct(1,2,3))); + } /** * Represent a _sequence with specified _tag, representing children first. @@ -293,33 +290,6 @@ final class Representer * Returns: The represented node. * * Throws: $(D RepresenterException) if a child could not be represented. - * - * Example: - * -------------------- - * struct MyStruct - * { - * int x, y, z; - * - * //Any D:YAML type must have a custom opCmp operator. - * //This is used for ordering in mappings. - * const int opCmp(ref const MyStruct s) - * { - * if(x != s.x){return x - s.x;} - * if(y != s.y){return y - s.y;} - * if(z != s.z){return z - s.z;} - * return 0; - * } - * } - * - * Node representMyStruct(ref Node node, Representer representer) - * { - * auto value = node.as!MyStruct; - * auto nodes = [Node(value.x), Node(value.y), Node(value.z)]; - * //use flow style - * return representer.representSequence("!mystruct.tag", nodes, - * CollectionStyle.Flow); - * } - * -------------------- */ Node representSequence(string tag, Node[] sequence, CollectionStyle style = CollectionStyle.Invalid) @trusted @@ -341,14 +311,46 @@ final class Representer if(style == CollectionStyle.Invalid) { - style = defaultCollectionStyle_ != CollectionStyle.Invalid + style = defaultCollectionStyle_ != CollectionStyle.Invalid ? defaultCollectionStyle_ : bestStyle; } return Node.rawNode(Node.Value(value), Mark(), tag, ScalarStyle.Invalid, style); } + /// + @safe unittest + { + struct MyStruct + { + int x, y, z; + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } + } + + static Node representMyStruct(ref Node node, Representer representer) + { + auto value = node.as!MyStruct; + auto nodes = [Node(value.x), Node(value.y), Node(value.z)]; + //use flow style + return representer.representSequence("!mystruct.tag", nodes, + CollectionStyle.Flow); + } + + auto dumper = Dumper("example.yaml"); + auto representer = new Representer; + representer.addRepresenter!MyStruct(&representMyStruct); + dumper.representer = representer; + dumper.dump(Node(MyStruct(1,2,3))); + } /** * Represent a mapping with specified _tag, representing children first. * @@ -362,33 +364,6 @@ final class Representer * Returns: The represented node. * * Throws: $(D RepresenterException) if a child could not be represented. - * - * Example: - * -------------------- - * struct MyStruct - * { - * int x, y, z; - * - * //Any D:YAML type must have a custom opCmp operator. - * //This is used for ordering in mappings. - * const int opCmp(ref const MyStruct s) - * { - * if(x != s.x){return x - s.x;} - * if(y != s.y){return y - s.y;} - * if(z != s.z){return z - s.z;} - * return 0; - * } - * } - * - * Node representMyStruct(ref Node node, Representer representer) - * { - * auto value = node.as!MyStruct; - * auto pairs = [Node.Pair("x", value.x), - * Node.Pair("y", value.y), - * Node.Pair("z", value.z)]; - * return representer.representMapping("!mystruct.tag", pairs); - * } - * -------------------- */ Node representMapping(string tag, Node.Pair[] pairs, CollectionStyle style = CollectionStyle.Invalid) @trusted @@ -425,6 +400,39 @@ final class Representer return Node.rawNode(Node.Value(value), Mark(), tag, ScalarStyle.Invalid, style); } + /// + @safe unittest + { + struct MyStruct + { + int x, y, z; + + //Any D:YAML type must have a custom opCmp operator. + //This is used for ordering in mappings. + const int opCmp(ref const MyStruct s) + { + if(x != s.x){return x - s.x;} + if(y != s.y){return y - s.y;} + if(z != s.z){return z - s.z;} + return 0; + } + } + + static Node representMyStruct(ref Node node, Representer representer) + { + auto value = node.as!MyStruct; + auto pairs = [Node.Pair("x", value.x), + Node.Pair("y", value.y), + Node.Pair("z", value.z)]; + return representer.representMapping("!mystruct.tag", pairs); + } + + auto dumper = Dumper("example.yaml"); + auto representer = new Representer; + representer.addRepresenter!MyStruct(&representMyStruct); + dumper.representer = representer; + dumper.dump(Node(MyStruct(1,2,3))); + } package: //Represent a node based on its type, and return the represented result. diff --git a/source/dyaml/resolver.d b/source/dyaml/resolver.d index d107266..5dd46fc 100644 --- a/source/dyaml/resolver.d +++ b/source/dyaml/resolver.d @@ -92,28 +92,6 @@ final class Resolver * regexp = Regular expression the scalar must match to have this _tag. * first = String of possible starting characters of the scalar. * - * Examples: - * - * Resolve scalars starting with 'A' to !_tag : - * -------------------- - * import std.regex; - * - * import dyaml.all; - * - * void main() - * { - * auto loader = Loader("file.txt"); - * auto resolver = new Resolver(); - * resolver.addImplicitResolver("!tag", std.regex.regex("A.*"), "A"); - * loader.resolver = resolver; - * - * //Note that we have no constructor from tag "!tag", so we can't - * //actually load anything that resolves to this tag. - * //See Constructor API documentation and tutorial for more information. - * - * auto node = loader.load(); - * } - * -------------------- */ void addImplicitResolver(string tag, Regex!char regexp, string first) pure @safe @@ -127,6 +105,26 @@ final class Resolver yamlImplicitResolvers_[c] ~= tuple(tag, regexp); } } + /// Resolve scalars starting with 'A' to !_tag + unittest { + import std.file : write; + import std.regex; + + import dyaml; + + write("example.yaml", "A"); + + auto loader = Loader("example.yaml"); + auto resolver = new Resolver(); + resolver.addImplicitResolver("!tag", regex("A.*"), "A"); + loader.resolver = resolver; + + //Note that we have no constructor from tag "!tag", so we can't + //actually load anything that resolves to this tag. + //See Constructor API documentation and tutorial for more information. + + //auto node = loader.load(); + } package: /* diff --git a/source/dyaml/token.d b/source/dyaml/token.d index 7a49d0b..03654af 100644 --- a/source/dyaml/token.d +++ b/source/dyaml/token.d @@ -103,6 +103,7 @@ static assert(Token.sizeof <= 32, "Token has unexpected size"); /// end = End position of the token. /// value = Value of the token. /// directive = Directive type (YAML or TAG in YAML 1.1). +/// nameEnd = Position of the end of the name Token directiveToken(const Mark start, const Mark end, char[] value, DirectiveType directive, const uint nameEnd) @safe pure nothrow @nogc {