Reverted doc style due to DDoc issues.

This commit is contained in:
Ferdinand Majerech 2014-08-01 02:51:35 +02:00
parent fdf4cecddb
commit 0f017646fc
4 changed files with 665 additions and 659 deletions

View file

@ -51,19 +51,20 @@ package class ConstructorException : YAMLException
private alias ConstructorException Error; private alias ConstructorException Error;
/// Constructs YAML values. /** Constructs YAML values.
/// *
/// Each YAML scalar, sequence or mapping has a tag specifying its data type. * Each YAML scalar, sequence or mapping has a tag specifying its data type.
/// Constructor uses user-specifyable functions to create a node of desired * Constructor uses user-specifyable functions to create a node of desired
/// data type from a scalar, sequence or mapping. * data type from a scalar, sequence or mapping.
/// *
/// *
/// Each of these functions is associated with a tag, and can process either * Each of these functions is associated with a tag, and can process either
/// a scalar, a sequence, or a mapping. The constructor passes each value to * a scalar, a sequence, or a mapping. The constructor passes each value to
/// the function with corresponding tag, which then returns the resulting value * the function with corresponding tag, which then returns the resulting value
/// that can be stored in a node. * that can be stored in a node.
/// *
/// If a tag is detected with no known constructor function, it is considered an error. * If a tag is detected with no known constructor function, it is considered an error.
*/
final class Constructor final class Constructor
{ {
private: private:
@ -116,72 +117,73 @@ final class Constructor
fromMapping_ = null; fromMapping_ = null;
} }
/// Add a constructor function from scalar. /** Add a constructor function from scalar.
/// *
/// The function must take a reference to $(D Node) to construct from. * The function must take a reference to $(D Node) to construct from.
/// The node contains a string for scalars, $(Node[]) for sequences and * The node contains a string for scalars, $(D Node[]) for sequences and
/// $(Node.Pair[]) for mappings. * $(D Node.Pair[]) for mappings.
/// *
/// Any exception thrown by this function will be caught by D:YAML and * Any exception thrown by this function will be caught by D:YAML and
/// its message will be added to a $(YAMLException) that will also tell * its message will be added to a $(D YAMLException) that will also tell
/// the user which type failed to construct, and position in the file. * the user which type failed to construct, and position in the file.
/// *
/// *
/// The value returned by this function will be stored in the resulting node. * The value returned by this function will be stored in the resulting node.
/// *
/// Only one constructor function can be set for one tag. * Only one constructor function can be set for one tag.
/// *
/// *
/// Structs and classes must implement the $(D opCmp()) operator for D:YAML * Structs and classes must implement the $(D opCmp()) operator for D:YAML
/// support. The signature of the operator that must be implemented * support. The signature of the operator that must be implemented
/// is $(D const int opCmp(ref const MyStruct s)) for structs where * is $(D const int opCmp(ref const MyStruct s)) for structs where
/// $(I MyStruct) is the struct type, and $(D int opCmp(Object o)) for * $(I MyStruct) is the struct type, and $(D int opCmp(Object o)) for
/// classes. Note that the class $(D opCmp()) should not alter the compared * classes. Note that the class $(D opCmp()) should not alter the compared
/// values - it is not const for compatibility reasons. * values - it is not const for compatibility reasons.
/// *
/// Params: tag = Tag for the function to handle. * Params: tag = Tag for the function to handle.
/// ctor = Constructor function. * ctor = Constructor function.
/// *
/// Example: * Example:
/// *
/// -------------------- * --------------------
/// import std.string; * import std.string;
/// *
/// import dyaml.all; * import dyaml.all;
/// *
/// struct MyStruct * struct MyStruct
/// { * {
/// int x, y, z; * int x, y, z;
/// *
/// //Any D:YAML type must have a custom opCmp operator. * //Any D:YAML type must have a custom opCmp operator.
/// //This is used for ordering in mappings. * //This is used for ordering in mappings.
/// const int opCmp(ref const MyStruct s) * const int opCmp(ref const MyStruct s)
/// { * {
/// if(x != s.x){return x - s.x;} * if(x != s.x){return x - s.x;}
/// if(y != s.y){return y - s.y;} * if(y != s.y){return y - s.y;}
/// if(z != s.z){return z - s.z;} * if(z != s.z){return z - s.z;}
/// return 0; * return 0;
/// } * }
/// } * }
/// *
/// MyStruct constructMyStructScalar(ref Node node) * MyStruct constructMyStructScalar(ref Node node)
/// { * {
/// //Guaranteed to be string as we construct from scalar. * //Guaranteed to be string as we construct from scalar.
/// //!mystruct x:y:z * //!mystruct x:y:z
/// auto parts = node.as!string().split(":"); * auto parts = node.as!string().split(":");
/// // If this throws, the D:YAML will handle it and throw a YAMLException. * // 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])); * return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
/// } * }
/// *
/// void main() * void main()
/// { * {
/// auto loader = Loader("file.yaml"); * auto loader = Loader("file.yaml");
/// auto constructor = new Constructor; * auto constructor = new Constructor;
/// constructor.addConstructorScalar("!mystruct", &constructMyStructScalar); * constructor.addConstructorScalar("!mystruct", &constructMyStructScalar);
/// loader.constructor = constructor; * loader.constructor = constructor;
/// Node node = loader.load(); * Node node = loader.load();
/// } * }
/// -------------------- * --------------------
*/
void addConstructorScalar(T)(const string tag, T function(ref Node) ctor) void addConstructorScalar(T)(const string tag, T function(ref Node) ctor)
@safe nothrow @safe nothrow
{ {
@ -190,48 +192,49 @@ final class Constructor
(*delegates!string)[t] = deleg; (*delegates!string)[t] = deleg;
} }
/// Add a constructor function from sequence. /** Add a constructor function from sequence.
/// *
/// See_Also: addConstructorScalar * See_Also: addConstructorScalar
/// *
/// Example: * Example:
/// *
/// -------------------- * --------------------
/// import std.string; * import std.string;
/// *
/// import dyaml.all; * import dyaml.all;
/// *
/// struct MyStruct * struct MyStruct
/// { * {
/// int x, y, z; * int x, y, z;
/// *
/// //Any D:YAML type must have a custom opCmp operator. * //Any D:YAML type must have a custom opCmp operator.
/// //This is used for ordering in mappings. * //This is used for ordering in mappings.
/// const int opCmp(ref const MyStruct s) * const int opCmp(ref const MyStruct s)
/// { * {
/// if(x != s.x){return x - s.x;} * if(x != s.x){return x - s.x;}
/// if(y != s.y){return y - s.y;} * if(y != s.y){return y - s.y;}
/// if(z != s.z){return z - s.z;} * if(z != s.z){return z - s.z;}
/// return 0; * return 0;
/// } * }
/// } * }
/// *
/// MyStruct constructMyStructSequence(ref Node node) * MyStruct constructMyStructSequence(ref Node node)
/// { * {
/// //node is guaranteed to be sequence. * //node is guaranteed to be sequence.
/// //!mystruct [x, y, z] * //!mystruct [x, y, z]
/// return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int); * return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
/// } * }
/// *
/// void main() * void main()
/// { * {
/// auto loader = Loader("file.yaml"); * auto loader = Loader("file.yaml");
/// auto constructor = new Constructor; * auto constructor = new Constructor;
/// constructor.addConstructorSequence("!mystruct", &constructMyStructSequence); * constructor.addConstructorSequence("!mystruct", &constructMyStructSequence);
/// loader.constructor = constructor; * loader.constructor = constructor;
/// Node node = loader.load(); * Node node = loader.load();
/// } * }
/// -------------------- * --------------------
*/
void addConstructorSequence(T)(const string tag, T function(ref Node) ctor) void addConstructorSequence(T)(const string tag, T function(ref Node) ctor)
@safe nothrow @safe nothrow
{ {
@ -240,48 +243,49 @@ final class Constructor
(*delegates!(Node[]))[t] = deleg; (*delegates!(Node[]))[t] = deleg;
} }
/// Add a constructor function from a mapping. /** Add a constructor function from a mapping.
/// *
/// See_Also: addConstructorScalar * See_Also: addConstructorScalar
/// *
/// Example: * Example:
/// *
/// -------------------- * --------------------
/// import std.string; * import std.string;
/// *
/// import dyaml.all; * import dyaml.all;
/// *
/// struct MyStruct * struct MyStruct
/// { * {
/// int x, y, z; * int x, y, z;
/// *
/// //Any D:YAML type must have a custom opCmp operator. * //Any D:YAML type must have a custom opCmp operator.
/// //This is used for ordering in mappings. * //This is used for ordering in mappings.
/// const int opCmp(ref const MyStruct s) * const int opCmp(ref const MyStruct s)
/// { * {
/// if(x != s.x){return x - s.x;} * if(x != s.x){return x - s.x;}
/// if(y != s.y){return y - s.y;} * if(y != s.y){return y - s.y;}
/// if(z != s.z){return z - s.z;} * if(z != s.z){return z - s.z;}
/// return 0; * return 0;
/// } * }
/// } * }
/// *
/// MyStruct constructMyStructMapping(ref Node node) * MyStruct constructMyStructMapping(ref Node node)
/// { * {
/// //node is guaranteed to be mapping. * //node is guaranteed to be mapping.
/// //!mystruct {"x": x, "y": y, "z": z} * //!mystruct {"x": x, "y": y, "z": z}
/// return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int); * return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
/// } * }
/// *
/// void main() * void main()
/// { * {
/// auto loader = Loader("file.yaml"); * auto loader = Loader("file.yaml");
/// auto constructor = new Constructor; * auto constructor = new Constructor;
/// constructor.addConstructorMapping("!mystruct", &constructMyStructMapping); * constructor.addConstructorMapping("!mystruct", &constructMyStructMapping);
/// loader.constructor = constructor; * loader.constructor = constructor;
/// Node node = loader.load(); * Node node = loader.load();
/// } * }
/// -------------------- * --------------------
*/
void addConstructorMapping(T)(const string tag, T function(ref Node) ctor) void addConstructorMapping(T)(const string tag, T function(ref Node) ctor)
@safe nothrow @safe nothrow
{ {

View file

@ -4,8 +4,7 @@
// http://www.boost.org/LICENSE_1_0.txt) // http://www.boost.org/LICENSE_1_0.txt)
/// Functionality that may be sometimes needed but allows unsafe or unstandard /// Functionality that may sometimes be needed but allows unsafe or unstandard behavior, and should only be used in specific cases.
/// behavior, and should only be used in specific cases.
module dyaml.hacks; module dyaml.hacks;
@ -15,25 +14,26 @@ import dyaml.node;
import dyaml.style; import dyaml.style;
/// Get the scalar style a YAML node had in the file it was loaded from. /** Get the scalar style a node had in the file it was loaded from.
/// *
/// This is only useful for nodes loaded from files. * This is only useful for nodes loaded from files.
/// *
/// This is a "hack" because a YAML application is supposed to be unaware of styles * This is a "hack" because a YAML application is supposed to be unaware of styles
/// used in YAML styles, i.e. treating different styles differently is unstandard. * used in YAML styles, i.e. treating different styles differently is unstandard.
/// However, determining style may be useful in some cases, e.g. YAML utilities. * 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). * May only be called on scalar nodes (nodes where node.isScalar() == true).
/// *
/// Example: * Example:
/// -------------------- * --------------------
/// // Node node // loaded from a file * // Node node // loaded from a file
/// if(node.isScalar) * if(node.isScalar)
/// { * {
/// import std.stdio; * import std.stdio;
/// writeln(node.scalarStyleHack()); * writeln(node.scalarStyleHack());
/// } * }
/// -------------------- * --------------------
*/
ScalarStyle scalarStyleHack(ref const(Node) node) @safe nothrow ScalarStyle scalarStyleHack(ref const(Node) node) @safe nothrow
{ {
assert(node.isScalar, "Trying to get scalar style of a non-scalar node"); assert(node.isScalar, "Trying to get scalar style of a non-scalar node");
@ -64,7 +64,7 @@ unittest
} }
/// Set the scalar style a YAML node had in the file it was loaded from. /// Set the scalar style node should have when written to a file.
/// ///
/// Setting the style might be useful when generating YAML or reformatting existing /// Setting the style might be useful when generating YAML or reformatting existing
/// files. /// files.
@ -84,7 +84,7 @@ unittest
assert(node.scalarStyleHack() == ScalarStyle.DoubleQuoted); assert(node.scalarStyleHack() == ScalarStyle.DoubleQuoted);
} }
/// Set the scalar style a YAML node had in the file it was loaded from. /// Set the collection style node should have when written to a file.
/// ///
/// Setting the style might be useful when generating YAML or reformatting existing /// Setting the style might be useful when generating YAML or reformatting existing
/// files. /// files.

View file

@ -24,78 +24,79 @@ import dyaml.scanner;
import dyaml.token; import dyaml.token;
/// Loads YAML documents from files or streams. /** Loads YAML documents from files or streams.
/// *
/// User specified Constructor and/or Resolver can be used to support new * User specified Constructor and/or Resolver can be used to support new
/// tags / data types. * tags / data types.
/// *
/// Examples: * Examples:
/// *
/// Load single YAML document from a file: * Load single YAML document from a file:
/// -------------------- * --------------------
/// auto rootNode = Loader("file.yaml").load(); * auto rootNode = Loader("file.yaml").load();
/// ... * ...
/// -------------------- * --------------------
/// *
/// Load all YAML documents from a file: * Load all YAML documents from a file:
/// -------------------- * --------------------
/// auto nodes = Loader("file.yaml").loadAll(); * auto nodes = Loader("file.yaml").loadAll();
/// ... * ...
/// -------------------- * --------------------
/// *
/// Iterate over YAML documents in a file, lazily loading them: * Iterate over YAML documents in a file, lazily loading them:
/// -------------------- * --------------------
/// auto loader = Loader("file.yaml"); * auto loader = Loader("file.yaml");
/// *
/// foreach(ref node; loader) * foreach(ref node; loader)
/// { * {
/// ... * ...
/// } * }
/// -------------------- * --------------------
/// *
/// Load YAML from a string: * Load YAML from a string:
/// -------------------- * --------------------
/// char[] yaml_input = "red: '#ff0000'\n" * char[] yaml_input = "red: '#ff0000'\n"
/// "green: '#00ff00'\n" * "green: '#00ff00'\n"
/// "blue: '#0000ff'".dup; * "blue: '#0000ff'".dup;
/// *
/// auto colors = Loader.fromString(yaml_input).load(); * auto colors = Loader.fromString(yaml_input).load();
/// *
/// foreach(string color, string value; colors) * foreach(string color, string value; colors)
/// { * {
/// import std.stdio; * import std.stdio;
/// writeln(color, " is ", value, " in HTML/CSS"); * writeln(color, " is ", value, " in HTML/CSS");
/// } * }
/// -------------------- * --------------------
/// *
/// Load a file into a buffer in memory and then load YAML from that buffer: * Load a file into a buffer in memory and then load YAML from that buffer:
/// -------------------- * --------------------
/// try * try
/// { * {
/// import std.file; * import std.file;
/// void[] buffer = std.file.read("file.yaml"); * void[] buffer = std.file.read("file.yaml");
/// auto yamlNode = Loader(buffer); * auto yamlNode = Loader(buffer);
/// *
/// // Read data from yamlNode here... * // Read data from yamlNode here...
/// } * }
/// catch(FileException e) * catch(FileException e)
/// { * {
/// writeln("Failed to read file 'file.yaml'"); * writeln("Failed to read file 'file.yaml'");
/// } * }
/// -------------------- * --------------------
/// *
/// Use a custom constructor/resolver to support custom data types and/or implicit tags: * Use a custom constructor/resolver to support custom data types and/or implicit tags:
/// -------------------- * --------------------
/// auto constructor = new Constructor(); * auto constructor = new Constructor();
/// auto resolver = new Resolver(); * auto resolver = new Resolver();
/// *
/// // Add constructor functions / resolver expressions here... * // Add constructor functions / resolver expressions here...
/// *
/// auto loader = Loader("file.yaml"); * auto loader = Loader("file.yaml");
/// loader.constructor = constructor; * loader.constructor = constructor;
/// loader.resolver = resolver; * loader.resolver = resolver;
/// auto rootNode = loader.load(node); * auto rootNode = loader.load(node);
/// -------------------- * --------------------
*/
struct Loader struct Loader
{ {
private: private:
@ -119,11 +120,12 @@ struct Loader
@disable int opCmp(ref Loader); @disable int opCmp(ref Loader);
@disable bool opEquals(ref Loader); @disable bool opEquals(ref Loader);
/// Construct a Loader to load YAML from a file. /** Construct a Loader to load YAML from a file.
/// *
/// Params: filename = Name of the file to load from. * Params: filename = Name of the file to load from.
/// *
/// Throws: YAMLException if the file could not be opened or read. * Throws: YAMLException if the file could not be opened or read.
*/
this(string filename) @trusted this(string filename) @trusted
{ {
name_ = filename; name_ = filename;
@ -144,17 +146,18 @@ struct Loader
return Loader(cast(ubyte[])data.dup); return Loader(cast(ubyte[])data.dup);
} }
/// Construct a Loader to load YAML from a string (char []). /** Construct a Loader to load YAML from a string (char []).
/// *
/// Params: data = String to load YAML from. $(B will) be overwritten during * Params: data = String to load YAML from. $(B will) be overwritten during
/// parsing as D:YAML reuses memory. Use data.dup if you don't * parsing as D:YAML reuses memory. Use data.dup if you don't
/// want to modify the original string. * want to modify the original string.
/// *
/// Returns: Loader loading YAML from given string. * Returns: Loader loading YAML from given string.
/// *
/// Throws: * Throws:
/// *
/// YAMLException if data could not be read (e.g. a decoding error) * YAMLException if data could not be read (e.g. a decoding error)
*/
static Loader fromString(char[] data) @safe static Loader fromString(char[] data) @safe
{ {
return Loader(cast(ubyte[])data); return Loader(cast(ubyte[])data);
@ -186,19 +189,21 @@ struct Loader
} }
} }
/// Construct a Loader to load YAML from a buffer. /** Construct a Loader to load YAML from a buffer.
/// *
/// Params: yamlData = Buffer with YAML data to load. This may be e.g. a file * 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 * loaded to memory or a string with YAML data. Note that
/// buffer $(B will) be overwritten, as D:YAML minimizes * buffer $(B will) be overwritten, as D:YAML minimizes
/// memory allocations by reusing the input _buffer. * memory allocations by reusing the input _buffer.
/// *
/// D:YAML looks for byte-order-makrs YAML files encoded in UTF-16/UTF-32 *
/// (and sometimes UTF-8) use to specify the encoding and endianness, so it * Note that D:YAML looks for byte-order-marks YAML files encoded in
/// should be enough to load an entire file to a buffer and pass it to D:YAML, * UTF-16/UTF-32 (and sometimes UTF-8) use to specify the encoding and
/// regardless of Unicode encoding. * 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. *
* Throws: YAMLException if yamlData contains data illegal in YAML.
*/
this(void[] yamlData) @safe this(void[] yamlData) @safe
{ {
try try
@ -242,16 +247,17 @@ struct Loader
constructor_ = constructor; constructor_ = constructor;
} }
/// Load single YAML document. /** Load single YAML document.
/// *
/// If none or more than one YAML document is found, this throws a YAMLException. * 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. * This can only be called once; this is enforced by contract.
/// *
/// Returns: Root node of the document. * Returns: Root node of the document.
/// *
/// Throws: YAMLException if there wasn't exactly one document * Throws: YAMLException if there wasn't exactly one document
/// or on a YAML parsing error. * or on a YAML parsing error.
*/
Node load() @safe Node load() @safe
in in
{ {
@ -273,17 +279,18 @@ struct Loader
} }
} }
/// Load all YAML documents. /** Load all YAML documents.
/// *
/// This is just a shortcut that iterates over all documents and returns them * This is just a shortcut that iterates over all documents and returns them
/// all at once. Calling loadAll after iterating over the node or vice versa * all at once. Calling loadAll after iterating over the node or vice versa
/// will not return any documents, as they have all been parsed already. * will not return any documents, as they have all been parsed already.
/// *
/// This can only be called once; this is enforced by contract. * This can only be called once; this is enforced by contract.
/// *
/// Returns: Array of root nodes of all documents in the file/stream. * Returns: Array of root nodes of all documents in the file/stream.
/// *
/// Throws: YAMLException on a parsing error. * Throws: YAMLException on a parsing error.
*/
Node[] loadAll() @trusted Node[] loadAll() @trusted
{ {
Node[] nodes; Node[] nodes;
@ -295,13 +302,14 @@ struct Loader
return nodes; return nodes;
} }
/// Foreach over YAML documents. /** Foreach over YAML documents.
/// *
/// Parses documents lazily, when they are needed. * Parses documents lazily, when they are needed.
/// *
/// Foreach over a Loader can only be used once; this is enforced by contract. * Foreach over a Loader can only be used once; this is enforced by contract.
/// *
/// Throws: YAMLException on a parsing error. * Throws: YAMLException on a parsing error.
*/
int opApply(int delegate(ref Node) dg) @trusted int opApply(int delegate(ref Node) dg) @trusted
in in
{ {

View file

@ -178,11 +178,12 @@ private struct Pair
} }
} }
/// YAML node. /** YAML node.
/// *
/// This is a pseudo-dynamic type that can store any YAML value, including a * This is a pseudo-dynamic type that can store any YAML value, including a
/// sequence or mapping of nodes. You can get data from a Node directly or * sequence or mapping of nodes. You can get data from a Node directly or
/// iterate over it if it's a collection. * iterate over it if it's a collection.
*/
struct Node struct Node
{ {
public: public:
@ -220,25 +221,26 @@ struct Node
static assert(Node.sizeof <= 48, "Unexpected YAML node size"); static assert(Node.sizeof <= 48, "Unexpected YAML node size");
public: public:
/// Construct a Node from a value. /** Construct a Node from a value.
/// *
/// Any type except for Node can be stored in a Node, but default YAML * Any type except for Node can be stored in a Node, but default YAML
/// types (integers, floats, strings, timestamps, etc.) will be stored * types (integers, floats, strings, timestamps, etc.) will be stored
/// more efficiently. To create a node representing a null value, * more efficiently. To create a node representing a null value,
/// construct it from YAMLNull. * construct it from YAMLNull.
/// *
/// *
/// Note that to emit any non-default types you store * Note that to emit any non-default types you store
/// in a node, you need a Representer to represent them in YAML - * in a node, you need a Representer to represent them in YAML -
/// otherwise emitting will fail. * otherwise emitting will fail.
/// *
/// Params: value = Value to store in the node. * Params: value = Value to store in the node.
/// tag = Overrides tag of the node when emitted, regardless * tag = Overrides tag of the node when emitted, regardless
/// of tag determined by Representer. Representer uses * of tag determined by Representer. Representer uses
/// this to determine YAML data type when a D data type * this to determine YAML data type when a D data type
/// maps to multiple different YAML data types. Tag must * maps to multiple different YAML data types. Tag must
/// be in full form, e.g. "tag:yaml.org,2002:int", not * be in full form, e.g. "tag:yaml.org,2002:int", not
/// a shortcut, like "!!int". * a shortcut, like "!!int".
*/
this(T)(T value, const string tag = null) @trusted this(T)(T value, const string tag = null) @trusted
if (isSomeString!T || (!isArray!T && !isAssociativeArray!T)) if (isSomeString!T || (!isArray!T && !isAssociativeArray!T))
{ {
@ -272,30 +274,31 @@ struct Node
} }
} }
/// Construct a node from an _array. /** Construct a node from an _array.
/// *
/// If _array is an _array of nodes or pairs, it is stored directly. * If _array is an _array of nodes or pairs, it is stored directly.
/// Otherwise, every value in the array is converted to a node, and * Otherwise, every value in the array is converted to a node, and
/// those nodes are stored. * those nodes are stored.
/// *
/// Params: array = Values to store in the node. * Params: array = Values to store in the node.
/// tag = Overrides tag of the node when emitted, regardless * tag = Overrides tag of the node when emitted, regardless
/// of tag determined by Representer. Representer uses * of tag determined by Representer. Representer uses
/// this to determine YAML data type when a D data type * this to determine YAML data type when a D data type
/// maps to multiple different YAML data types. * maps to multiple different YAML data types.
/// This is used to differentiate between YAML sequences * This is used to differentiate between YAML sequences
/// ("!!seq") and sets ("!!set"), which both are * ("!!seq") and sets ("!!set"), which both are
/// internally represented as an array_ of nodes. Tag * internally represented as an array_ of nodes. Tag
/// must be in full form, e.g. "tag:yaml.org,2002:set", * must be in full form, e.g. "tag:yaml.org,2002:set",
/// not a shortcut, like "!!set". * not a shortcut, like "!!set".
/// *
/// Examples: * Examples:
/// -------------------- * --------------------
/// // Will be emitted as a sequence (default for arrays) * // Will be emitted as a sequence (default for arrays)
/// auto seq = Node([1, 2, 3, 4, 5]); * auto seq = Node([1, 2, 3, 4, 5]);
/// // Will be emitted as a set (overriden tag) * // Will be emitted as a set (overriden tag)
/// auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set"); * auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set");
/// -------------------- * --------------------
*/
this(T)(T[] array, const string tag = null) @safe this(T)(T[] array, const string tag = null) @safe
if (!isSomeString!(T[])) if (!isSomeString!(T[]))
{ {
@ -333,32 +336,33 @@ struct Node
auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set"); auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set");
} }
/// Construct a node from an associative _array. /** Construct a node from an associative _array.
/// *
/// If keys and/or values of _array are nodes, they stored directly. * If keys and/or values of _array are nodes, they stored directly.
/// Otherwise they are converted to nodes and then stored. * Otherwise they are converted to nodes and then stored.
/// *
/// Params: array = Values to store in the node. * Params: array = Values to store in the node.
/// tag = Overrides tag of the node when emitted, regardless * tag = Overrides tag of the node when emitted, regardless
/// of tag determined by Representer. Representer uses * of tag determined by Representer. Representer uses
/// this to determine YAML data type when a D data type * this to determine YAML data type when a D data type
/// maps to multiple different YAML data types. * maps to multiple different YAML data types.
/// This is used to differentiate between YAML unordered * This is used to differentiate between YAML unordered
/// mappings ("!!map"), ordered mappings ("!!omap"), and * mappings ("!!map"), ordered mappings ("!!omap"), and
/// pairs ("!!pairs") which are all internally * pairs ("!!pairs") which are all internally
/// represented as an _array of node pairs. Tag must be * represented as an _array of node pairs. Tag must be
/// in full form, e.g. "tag:yaml.org,2002:omap", not a * in full form, e.g. "tag:yaml.org,2002:omap", not a
/// shortcut, like "!!omap". * shortcut, like "!!omap".
/// *
/// Examples: * Examples:
/// -------------------- * --------------------
/// // Will be emitted as an unordered mapping (default for mappings) * // Will be emitted as an unordered mapping (default for mappings)
/// auto map = Node([1 : "a", 2 : "b"]); * auto map = Node([1 : "a", 2 : "b"]);
/// // Will be emitted as an ordered map (overriden tag) * // Will be emitted as an ordered map (overriden tag)
/// auto omap = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:omap"); * auto omap = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:omap");
/// // Will be emitted as pairs (overriden tag) * // Will be emitted as pairs (overriden tag)
/// auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs"); * auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs");
/// -------------------- * --------------------
*/
this(K, V)(V[K] array, const string tag = null) @safe this(K, V)(V[K] array, const string tag = null) @safe
{ {
tag_ = Tag(tag); tag_ = Tag(tag);
@ -387,41 +391,42 @@ struct Node
auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs"); auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs");
} }
/// Construct a node from arrays of _keys and _values. /** Construct a node from arrays of _keys and _values.
/// *
/// Constructs a mapping node with key-value pairs from * Constructs a mapping node with key-value pairs from
/// _keys and _values, keeping their order. Useful when order * _keys and _values, keeping their order. Useful when order
/// is important (ordered maps, pairs). * is important (ordered maps, pairs).
/// *
/// *
/// keys and values must have equal length. * keys and values must have equal length.
/// *
/// *
/// If _keys and/or _values are nodes, they are stored directly/ * If _keys and/or _values are nodes, they are stored directly/
/// Otherwise they are converted to nodes and then stored. * Otherwise they are converted to nodes and then stored.
/// *
/// Params: keys = Keys of the mapping, from first to last pair. * Params: keys = Keys of the mapping, from first to last pair.
/// values = Values of the mapping, from first to last pair. * values = Values of the mapping, from first to last pair.
/// tag = Overrides tag of the node when emitted, regardless * tag = Overrides tag of the node when emitted, regardless
/// of tag determined by Representer. Representer uses * of tag determined by Representer. Representer uses
/// this to determine YAML data type when a D data type * this to determine YAML data type when a D data type
/// maps to multiple different YAML data types. * maps to multiple different YAML data types.
/// This is used to differentiate between YAML unordered * This is used to differentiate between YAML unordered
/// mappings ("!!map"), ordered mappings ("!!omap"), and * mappings ("!!map"), ordered mappings ("!!omap"), and
/// pairs ("!!pairs") which are all internally * pairs ("!!pairs") which are all internally
/// represented as an array of node pairs. Tag must be * represented as an array of node pairs. Tag must be
/// in full form, e.g. "tag:yaml.org,2002:omap", not a * in full form, e.g. "tag:yaml.org,2002:omap", not a
/// shortcut, like "!!omap". * shortcut, like "!!omap".
/// *
/// Examples: * Examples:
/// -------------------- * --------------------
/// // Will be emitted as an unordered mapping (default for mappings) * // Will be emitted as an unordered mapping (default for mappings)
/// auto map = Node([1, 2], ["a", "b"]); * auto map = Node([1, 2], ["a", "b"]);
/// // Will be emitted as an ordered map (overriden tag) * // Will be emitted as an ordered map (overriden tag)
/// auto omap = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:omap"); * auto omap = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:omap");
/// // Will be emitted as pairs (overriden tag) * // Will be emitted as pairs (overriden tag)
/// auto pairs = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:pairs"); * auto pairs = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:pairs");
/// -------------------- * --------------------
*/
this(K, V)(K[] keys, V[] values, const string tag = null) @safe this(K, V)(K[] keys, V[] values, const string tag = null) @safe
if(!(isSomeString!(K[]) || isSomeString!(V[]))) if(!(isSomeString!(K[]) || isSomeString!(V[])))
in in
@ -494,32 +499,25 @@ struct Node
/// Return tag of the node. /// Return tag of the node.
@property string tag() const @safe nothrow {return tag_.get;} @property string tag() const @safe nothrow {return tag_.get;}
/// Equality test. /** Equality test.
/// *
/// If T is Node, recursively compare all subnodes. * If T is Node, recursively compares all subnodes.
/// This might be quite expensive if testing entire documents. * This might be quite expensive if testing entire documents.
/// *
/// If T is not Node, get a value if type T from the node and test * If T is not Node, gets a value of type T from the node and tests
/// equality with that. * equality with that.
/// *
/// To test equality with a null YAML value, use YAMLNull. * To test equality with a null YAML value, use YAMLNull.
/// *
/// Examples: * Params: rhs = Variable to test equality with.
/// -------------------- *
/// auto node = Node(42); * Returns: true if equal, false otherwise.
/// */
/// assert(node == 42);
/// assert(node != "42");
/// assert(node != "43");
/// --------------------
///
/// Params: rhs = Variable to test equality with.
///
/// Returns: true if equal, false otherwise.
bool opEquals(T)(const auto ref T rhs) const @safe bool opEquals(T)(const auto ref T rhs) const @safe
{ {
return equals!(Yes.useTag)(rhs); return equals!(Yes.useTag)(rhs);
} }
///
unittest unittest
{ {
auto node = Node(42); auto node = Node(42);
@ -535,48 +533,49 @@ struct Node
/// Shortcut for get(). /// Shortcut for get().
alias get as; alias get as;
/// Get the value of the node as specified type. /** Get the value of the node as specified type.
/// *
/// If the specifed type does not match type in the node, * If the specifed type does not match type in the node,
/// conversion is attempted. The stringConversion template * conversion is attempted. The stringConversion template
/// parameter can be used to disable conversion from non-string * parameter can be used to disable conversion from non-string
/// types to strings. * types to strings.
/// *
/// Numeric values are range checked, throwing if out of range of * Numeric values are range checked, throwing if out of range of
/// requested type. * requested type.
/// *
/// Timestamps are stored as std.datetime.SysTime. * Timestamps are stored as std.datetime.SysTime.
/// Binary values are decoded and stored as ubyte[]. * Binary values are decoded and stored as ubyte[].
/// *
/// To get a null value, use get!YAMLNull . This is to * To get a null value, use get!YAMLNull . This is to
/// prevent getting null values for types such as strings or classes. * prevent getting null values for types such as strings or classes.
/// *
/// $(BR)$(B Mapping default values:) * $(BR)$(B Mapping default values:)
/// *
/// $(PBR * $(PBR
/// The '=' key can be used to denote the default value of a mapping. * The '=' key can be used to denote the default value of a mapping.
/// This can be used when a node is scalar in early versions of a program, * This can be used when a node is scalar in early versions of a program,
/// but is replaced by a mapping later. Even if the node is a mapping, the * but is replaced by a mapping later. Even if the node is a mapping, the
/// get method can be used as if it was a scalar if it has a default value. * get method can be used as if it was a scalar if it has a default value.
/// This way, new YAML files where the node is a mapping can still be read * This way, new YAML files where the node is a mapping can still be read
/// by old versions of the program, which expect the node to be a scalar. * by old versions of the program, which expect the node to be a scalar.
/// ) * )
/// *
/// Examples: * Examples:
/// *
/// Automatic type conversion: * Automatic type conversion:
/// -------------------- * --------------------
/// auto node = Node(42); * auto node = Node(42);
/// *
/// assert(node.as!int == 42); * assert(node.as!int == 42);
/// assert(node.as!string == "42"); * assert(node.as!string == "42");
/// assert(node.as!double == 42.0); * assert(node.as!double == 42.0);
/// -------------------- * --------------------
/// *
/// Returns: Value of the node as specified type. * Returns: Value of the node as specified type.
/// *
/// Throws: NodeException if unable to convert to specified type, or if * Throws: NodeException if unable to convert to specified type, or if
/// the value is out of range of requested type. * the value is out of range of requested type.
*/
@property T get(T, Flag!"stringConversion" stringConversion = Yes.stringConversion)() @property T get(T, Flag!"stringConversion" stringConversion = Yes.stringConversion)()
@trusted if(!is(T == const)) @trusted if(!is(T == const))
{ {
@ -711,13 +710,14 @@ struct Node
} }
} }
/// If this is a collection, return its _length. /** If this is a collection, return its _length.
/// *
/// Otherwise, throw NodeException. * Otherwise, throw NodeException.
/// *
/// Returns: Number of elements in a sequence or key-value pairs in a mapping. * Returns: Number of elements in a sequence or key-value pairs in a mapping.
/// *
/// Throws: NodeException if this is not a sequence nor a mapping. * Throws: NodeException if this is not a sequence nor a mapping.
*/
@property size_t length() const @safe @property size_t length() const @safe
{ {
if(isSequence) {return value_.get!(const Node[]).length;} if(isSequence) {return value_.get!(const Node[]).length;}
@ -726,24 +726,25 @@ struct Node
startMark_); startMark_);
} }
/// Get the element at specified index. /** Get the element at specified index.
/// *
/// If the node is a sequence, index must be integral. * If the node is a sequence, index must be integral.
/// *
/// *
/// If the node is a mapping, return the value corresponding to the first * If the node is a mapping, return the value corresponding to the first
/// key equal to index. containsKey() can be used to determine if a mapping * key equal to index. containsKey() can be used to determine if a mapping
/// has a specific key. * has a specific key.
/// *
/// To get element at a null index, use YAMLNull for index. * To get element at a null index, use YAMLNull for index.
/// *
/// Params: index = Index to use. * Params: index = Index to use.
/// *
/// Returns: Value corresponding to the index. * Returns: Value corresponding to the index.
/// *
/// Throws: NodeException if the index could not be found, * Throws: NodeException if the index could not be found,
/// non-integral index is used with a sequence or the node is * non-integral index is used with a sequence or the node is
/// not a collection. * not a collection.
*/
ref Node opIndex(T)(T index) @trusted ref Node opIndex(T)(T index) @trusted
{ {
if(isSequence) if(isSequence)
@ -807,36 +808,31 @@ struct Node
assertThrown!NodeException(nmap[14]); assertThrown!NodeException(nmap[14]);
} }
/// Determine if a collection contains specified value. /** Determine if a collection contains specified value.
/// *
/// If the node is a sequence, check if it contains the specified value. * If the node is a sequence, check if it contains the specified value.
/// If it's a mapping, check if it has a value that matches specified value. * If it's a mapping, check if it has a value that matches specified value.
/// *
/// To check for a null value, use YAMLNull for rhs. * Params: rhs = Item to look for. Use YAMLNull to check for a null value.
/// *
/// Params: rhs = Item to look for. * Returns: true if rhs was found, false otherwise.
/// *
/// Returns: true if rhs was found, false otherwise. * Throws: NodeException if the node is not a collection.
/// */
/// Throws: NodeException if the node is not a collection.
bool contains(T)(T rhs) const @safe bool contains(T)(T rhs) const @safe
{ {
return contains_!(T, No.key, "contains")(rhs); return contains_!(T, No.key, "contains")(rhs);
} }
/// Determine if a collection contains specified key. /** Determine if a mapping contains specified key.
/// *
/// If the node is a mapping, check if it has a key * Params: rhs = Key to look for. Use YAMLNull to check for a null key.
/// that matches specified key. *
/// * Returns: true if rhs was found, false otherwise.
/// To check for a null key, use YAMLNull for rhs. *
/// * Throws: NodeException if the node is not a mapping.
/// Params: rhs = Item to look for. */
///
/// Returns: true if rhs was found, false otherwise.
///
/// Throws: NodeException if the node is not a mapping.
bool containsKey(T)(T rhs) const @safe bool containsKey(T)(T rhs) const @safe
{ {
return contains_!(T, Yes.key, "containsKey")(rhs); return contains_!(T, Yes.key, "containsKey")(rhs);
@ -921,26 +917,27 @@ struct Node
"Node.opAssign() doesn't produce an equivalent copy"); "Node.opAssign() doesn't produce an equivalent copy");
} }
/// Set element at specified index in a collection. /** Set element at specified index in a collection.
/// *
/// This method can only be called on collection nodes. * This method can only be called on collection nodes.
/// *
/// If the node is a sequence, index must be integral. * If the node is a sequence, index must be integral.
/// *
/// If the node is a mapping, sets the _value corresponding to the first * If the node is a mapping, sets the _value corresponding to the first
/// key matching index (including conversion, so e.g. "42" matches 42). * key matching index (including conversion, so e.g. "42" matches 42).
/// *
/// If the node is a mapping and no key matches index, a new key-value * If the node is a mapping and no key matches index, a new key-value
/// pair is added to the mapping. In sequences the index must be in * pair is added to the mapping. In sequences the index must be in
/// range. This ensures behavior siilar to D arrays and associative * range. This ensures behavior siilar to D arrays and associative
/// arrays. * arrays.
/// *
/// To set element at a null index, use YAMLNull for index. * To set element at a null index, use YAMLNull for index.
/// *
/// Params: index = Index of the value to set. * Params: index = Index of the value to set.
/// *
/// Throws: NodeException if the node is not a collection, index is out * 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. * of range or if a non-integral index is used on a sequence node.
*/
void opIndexAssign(K, V)(V value, K index) @safe void opIndexAssign(K, V)(V value, K index) @safe
{ {
if(isSequence()) if(isSequence())
@ -1005,13 +1002,14 @@ struct Node
} }
} }
/// Foreach over a sequence, getting each element as T. /** Foreach over a sequence, getting each element as T.
/// *
/// If T is Node, simply iterate over the nodes in the sequence. * If T is Node, simply iterate over the nodes in the sequence.
/// Otherwise, convert each node to T during iteration. * Otherwise, convert each node to T during iteration.
/// *
/// Throws: NodeException if the node is not a sequence or an * Throws: NodeException if the node is not a sequence or an
/// element could not be converted to specified type. * element could not be converted to specified type.
*/
int opApply(T)(int delegate(ref T) dg) @trusted int opApply(T)(int delegate(ref T) dg) @trusted
{ {
enforce(isSequence, enforce(isSequence,
@ -1060,13 +1058,14 @@ struct Node
assert(array2 == [11, 12, 13, 14]); assert(array2 == [11, 12, 13, 14]);
} }
/// Foreach over a mapping, getting each key/value as K/V. /** Foreach over a mapping, getting each key/value as K/V.
/// *
/// If the K and/or V is Node, simply iterate over the nodes in the mapping. * If the K and/or V is Node, simply iterate over the nodes in the mapping.
/// Otherwise, convert each key/value to T during iteration. * Otherwise, convert each key/value to T during iteration.
/// *
/// Throws: NodeException if the node is not a mapping or an * Throws: NodeException if the node is not a mapping or an
/// element could not be converted to specified type. * element could not be converted to specified type.
*/
int opApply(K, V)(int delegate(ref K, ref V) dg) @trusted int opApply(K, V)(int delegate(ref K, ref V) dg) @trusted
{ {
enforce(isMapping, enforce(isMapping,
@ -1152,19 +1151,20 @@ struct Node
} }
} }
/// Add an element to a sequence. /** Add an element to a sequence.
/// *
/// This method can only be called on sequence nodes. * This method can only be called on sequence nodes.
/// *
/// If value is a node, it is copied to the sequence directly. Otherwise * If value is a node, it is copied to the sequence directly. Otherwise
/// value is converted to a node and then stored in the sequence. * value is converted to a node and then stored in the sequence.
/// *
/// $(P When emitting, all values in the sequence will be emitted. When * $(P When emitting, all values in the sequence will be emitted. When
/// using the !!set tag, the user needs to ensure that all elements in * using the !!set tag, the user needs to ensure that all elements in
/// the sequence are unique, otherwise $(B invalid) YAML code will be * the sequence are unique, otherwise $(B invalid) YAML code will be
/// emitted.) * emitted.)
/// *
/// Params: value = Value to _add to the sequence. * Params: value = Value to _add to the sequence.
*/
void add(T)(T value) @safe void add(T)(T value) @safe
{ {
enforce(isSequence(), enforce(isSequence(),
@ -1186,20 +1186,21 @@ struct Node
} }
} }
/// Add a key-value pair to a mapping. /** Add a key-value pair to a mapping.
/// *
/// This method can only be called on mapping nodes. * This method can only be called on mapping nodes.
/// *
/// If key and/or value is a node, it is copied to the mapping directly. * If key and/or value is a node, it is copied to the mapping directly.
/// Otherwise it is converted to a node and then stored in the mapping. * Otherwise it is converted to a node and then stored in the mapping.
/// *
/// $(P It is possible for the same key to be present more than once in a * $(P It is possible for the same key to be present more than once in a
/// mapping. When emitting, all key-value pairs will be emitted. * mapping. When emitting, all key-value pairs will be emitted.
/// This is useful with the "!!pairs" tag, but will result in * This is useful with the "!!pairs" tag, but will result in
/// $(B invalid) YAML with "!!map" and "!!omap" tags.) * $(B invalid) YAML with "!!map" and "!!omap" tags.)
/// *
/// Params: key = Key to _add. * Params: key = Key to _add.
/// value = Value to _add. * value = Value to _add.
*/
void add(K, V)(K key, V value) @safe void add(K, V)(K key, V value) @safe
{ {
enforce(isMapping(), enforce(isMapping(),
@ -1221,20 +1222,22 @@ struct Node
} }
} }
/// Determine whether a key is in a mapping, and access its value. /** Determine whether a key is in a mapping, and access its value.
/// *
/// This method can only be called on mapping nodes. * This method can only be called on mapping nodes.
/// *
/// Params: key = Key to search for. * Params: key = Key to search for.
/// *
/// Returns: A pointer to the value (as a Node) corresponding to key, * Returns: A pointer to the value (as a Node) corresponding to key,
/// or null if not found. * or null if not found.
/// *
/// Note: Any modification to the node can invalidate the returned * Note: Any modification to the node can invalidate the returned
/// pointer. * pointer.
/// *
/// See_Also: contains * See_Also: contains
Node* opBinaryRight(string op, K)(K key) @trusted if (op == "in") */
Node* opBinaryRight(string op, K)(K key) @system
if (op == "in")
{ {
enforce(isMapping, new Error("Trying to use 'in' on a " ~ enforce(isMapping, new Error("Trying to use 'in' on a " ~
nodeTypeString ~ " node", startMark_)); nodeTypeString ~ " node", startMark_));
@ -1262,17 +1265,18 @@ struct Node
assert(mapping["foo"] == Node("newfoo")); assert(mapping["foo"] == Node("newfoo"));
} }
/// Remove first (if any) occurence of a value in a collection. /** Remove first (if any) occurence of a value in a collection.
/// *
/// This method can only be called on collection nodes. * This method can only be called on collection nodes.
/// *
/// If the node is a sequence, the first node matching value is removed. * If the node is a sequence, the first node matching value is removed.
/// If the node is a mapping, the first key-value pair where _value * If the node is a mapping, the first key-value pair where _value
/// matches specified value is removed. * matches specified value is removed.
/// *
/// Params: rhs = Value to _remove. * Params: rhs = Value to _remove.
/// *
/// Throws: NodeException if the node is not a collection. * Throws: NodeException if the node is not a collection.
*/
void remove(T)(T rhs) @trusted void remove(T)(T rhs) @trusted
{ {
remove_!(T, No.key, "remove")(rhs); remove_!(T, No.key, "remove")(rhs);
@ -1303,23 +1307,24 @@ struct Node
} }
} }
/// Remove element at the specified index of a collection. /** Remove element at the specified index of a collection.
/// *
/// This method can only be called on collection nodes. * This method can only be called on collection nodes.
/// *
/// If the node is a sequence, index must be integral. * If the node is a sequence, index must be integral.
/// *
/// If the node is a mapping, remove the first key-value pair where * If the node is a mapping, remove the first key-value pair where
/// key matches index. * key matches index.
/// *
/// If the node is a mapping and no key matches index, nothing is removed * If the node is a mapping and no key matches index, nothing is removed
/// and no exception is thrown. This ensures behavior siilar to D arrays * and no exception is thrown. This ensures behavior siilar to D arrays
/// and associative arrays. * and associative arrays.
/// *
/// Params: index = Index to remove at. * Params: index = Index to remove at.
/// *
/// Throws: NodeException if the node is not a collection, index is out * 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. * of range or if a non-integral index is used on a sequence node.
*/
void removeAt(T)(T index) @trusted void removeAt(T)(T index) @trusted
{ {
remove_!(T, Yes.key, "removeAt")(index); remove_!(T, Yes.key, "removeAt")(index);
@ -1355,26 +1360,15 @@ struct Node
} }
// Compute hash of the node. // Compute hash of the node.
hash_t toHash() const nothrow @safe hash_t toHash() @safe nothrow const
{
// Hack to allow const nothrow @safe.
// Should be rewritten once std.variant is fixed.
hash_t unsafeHash() nothrow @trusted
{ {
const tagHash = tag_.isNull ? 0 : tag_.toHash(); const tagHash = tag_.isNull ? 0 : tag_.toHash();
// Variant toHash is not nothrow at the moment, so we need to catch
// an exception that is never thrown.
try
{
// Variant toHash is not const at the moment, so we need to const-cast. // Variant toHash is not const at the moment, so we need to const-cast.
return tagHash + (cast(Value)value_).toHash(); return tagHash + value_.toHash();
} }
catch(Exception e) unittest
{ {
assert(false, "Unexpected exception caught"); writeln("Node(42).toHash(): ", Node(42).toHash());
}
}
return unsafeHash();
} }
package: package: