diff --git a/source/dyaml/constructor.d b/source/dyaml/constructor.d index e71f12b..d468817 100644 --- a/source/dyaml/constructor.d +++ b/source/dyaml/constructor.d @@ -51,19 +51,20 @@ package class ConstructorException : YAMLException private alias ConstructorException Error; -/// Constructs YAML values. -/// -/// Each YAML scalar, sequence or mapping has a tag specifying its data type. -/// Constructor uses user-specifyable functions to create a node of desired -/// data type from a scalar, sequence or mapping. -/// -/// -/// 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 -/// the function with corresponding tag, which then returns the resulting value -/// that can be stored in a node. -/// -/// If a tag is detected with no known constructor function, it is considered an error. +/** Constructs YAML values. + * + * Each YAML scalar, sequence or mapping has a tag specifying its data type. + * Constructor uses user-specifyable functions to create a node of desired + * data type from a scalar, sequence or mapping. + * + * + * 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 + * the function with corresponding tag, which then returns the resulting value + * that can be stored in a node. + * + * If a tag is detected with no known constructor function, it is considered an error. + */ final class Constructor { private: @@ -116,72 +117,73 @@ final class Constructor fromMapping_ = null; } - /// Add a constructor function from scalar. - /// - /// The function must take a reference to $(D Node) to construct from. - /// The node contains a string for scalars, $(Node[]) for sequences and - /// $(Node.Pair[]) for mappings. - /// - /// 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 - /// 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. - /// - /// Only one constructor function can be set for one tag. - /// - /// - /// Structs and classes must implement the $(D opCmp()) operator for D:YAML - /// support. The signature of the operator that must be implemented - /// 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 - /// classes. Note that the class $(D opCmp()) should not alter the compared - /// values - it is not const for compatibility reasons. - /// - /// 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(); - /// } - /// -------------------- + /** Add a constructor function from scalar. + * + * The function must take a reference to $(D Node) to construct from. + * The node contains a string for scalars, $(D Node[]) for sequences and + * $(D Node.Pair[]) for mappings. + * + * Any exception thrown by this function will be caught by D:YAML and + * 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 value returned by this function will be stored in the resulting node. + * + * Only one constructor function can be set for one tag. + * + * + * Structs and classes must implement the $(D opCmp()) operator for D:YAML + * support. The signature of the operator that must be implemented + * 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 + * classes. Note that the class $(D opCmp()) should not alter the compared + * values - it is not const for compatibility reasons. + * + * 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) @safe nothrow { @@ -190,48 +192,49 @@ final class Constructor (*delegates!string)[t] = deleg; } - /// 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(); - /// } - /// -------------------- + /** 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) @safe nothrow { @@ -240,48 +243,49 @@ final class Constructor (*delegates!(Node[]))[t] = deleg; } - /// 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(); - /// } - /// -------------------- + /** 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) @safe nothrow { diff --git a/source/dyaml/hacks.d b/source/dyaml/hacks.d index 47793d4..1280a20 100644 --- a/source/dyaml/hacks.d +++ b/source/dyaml/hacks.d @@ -4,8 +4,7 @@ // http://www.boost.org/LICENSE_1_0.txt) -/// Functionality that may be sometimes needed but allows unsafe or unstandard -/// behavior, and should only be used in specific cases. +/// Functionality that may sometimes be needed but allows unsafe or unstandard behavior, and should only be used in specific cases. module dyaml.hacks; @@ -15,25 +14,26 @@ import dyaml.node; import dyaml.style; -/// Get the scalar style a YAML node had in the file it was loaded from. -/// -/// This is only useful for nodes loaded from files. -/// -/// 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. -/// 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()); -/// } -/// -------------------- +/** 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 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. + * 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"); @@ -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 /// files. @@ -84,7 +84,7 @@ unittest 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 /// files. diff --git a/source/dyaml/loader.d b/source/dyaml/loader.d index 4e5989f..7383ef7 100644 --- a/source/dyaml/loader.d +++ b/source/dyaml/loader.d @@ -24,78 +24,79 @@ import dyaml.scanner; import dyaml.token; -/// Loads YAML documents from files or streams. -/// -/// 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); -/// -------------------- +/** Loads YAML documents from files or streams. + * + * 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 { private: @@ -119,11 +120,12 @@ struct Loader @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. - /// - /// Throws: YAMLException if the file could not be opened or read. + /** Construct a Loader to load YAML from a file. + * + * Params: filename = Name of the file to load from. + * + * Throws: YAMLException if the file could not be opened or read. + */ this(string filename) @trusted { name_ = filename; @@ -144,17 +146,18 @@ struct Loader return Loader(cast(ubyte[])data.dup); } - /// Construct a Loader to load YAML from a string (char []). - /// - /// 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 - /// want to modify the original string. - /// - /// Returns: Loader loading YAML from given string. - /// - /// Throws: - /// - /// YAMLException if data could not be read (e.g. a decoding error) + /** Construct a Loader to load YAML from a string (char []). + * + * 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 + * want to modify the original string. + * + * 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); @@ -186,19 +189,21 @@ struct Loader } } - /// 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. - /// - /// 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 - /// 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. + /** 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. + * + * + * 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. + */ this(void[] yamlData) @safe { try @@ -242,16 +247,17 @@ struct Loader constructor_ = constructor; } - /// 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. + /** 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 in { @@ -273,17 +279,18 @@ struct Loader } } - /// Load all YAML documents. - /// - /// 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 - /// will not return any documents, as they have all been parsed already. - /// - /// This can only be called once; this is enforced by contract. - /// - /// Returns: Array of root nodes of all documents in the file/stream. - /// - /// Throws: YAMLException on a parsing error. + /** Load all YAML documents. + * + * 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 + * will not return any documents, as they have all been parsed already. + * + * This can only be called once; this is enforced by contract. + * + * Returns: Array of root nodes of all documents in the file/stream. + * + * Throws: YAMLException on a parsing error. + */ Node[] loadAll() @trusted { Node[] nodes; @@ -295,13 +302,14 @@ struct Loader return nodes; } - /// Foreach over YAML documents. - /// - /// Parses documents lazily, when they are needed. - /// - /// Foreach over a Loader can only be used once; this is enforced by contract. - /// - /// Throws: YAMLException on a parsing error. + /** Foreach over YAML documents. + * + * Parses documents lazily, when they are needed. + * + * Foreach over a Loader can only be used once; this is enforced by contract. + * + * Throws: YAMLException on a parsing error. + */ int opApply(int delegate(ref Node) dg) @trusted in { diff --git a/source/dyaml/node.d b/source/dyaml/node.d index b4f37f8..730b763 100644 --- a/source/dyaml/node.d +++ b/source/dyaml/node.d @@ -178,11 +178,12 @@ private struct Pair } } -/// YAML node. -/// -/// 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 -/// iterate over it if it's a collection. +/** YAML node. + * + * 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 + * iterate over it if it's a collection. + */ struct Node { public: @@ -220,25 +221,26 @@ struct Node static assert(Node.sizeof <= 48, "Unexpected YAML node size"); public: - /// Construct a Node from a value. - /// - /// Any type except for Node can be stored in a Node, but default YAML - /// types (integers, floats, strings, timestamps, etc.) will be stored - /// more efficiently. To create a node representing a null value, - /// construct it from YAMLNull. - /// - /// - /// Note that to emit any non-default types you store - /// in a node, you need a Representer to represent them in YAML - - /// otherwise emitting will fail. - /// - /// Params: value = Value to store in the node. - /// tag = Overrides tag of the node when emitted, regardless - /// of tag determined by Representer. Representer uses - /// this to determine YAML data type when a D data type - /// maps to multiple different YAML data types. Tag must - /// be in full form, e.g. "tag:yaml.org,2002:int", not - /// a shortcut, like "!!int". + /** Construct a Node from a value. + * + * Any type except for Node can be stored in a Node, but default YAML + * types (integers, floats, strings, timestamps, etc.) will be stored + * more efficiently. To create a node representing a null value, + * construct it from YAMLNull. + * + * + * Note that to emit any non-default types you store + * in a node, you need a Representer to represent them in YAML - + * otherwise emitting will fail. + * + * Params: value = Value to store in the node. + * tag = Overrides tag of the node when emitted, regardless + * of tag determined by Representer. Representer uses + * this to determine YAML data type when a D data type + * maps to multiple different YAML data types. Tag must + * be in full form, e.g. "tag:yaml.org,2002:int", not + * a shortcut, like "!!int". + */ this(T)(T value, const string tag = null) @trusted if (isSomeString!T || (!isArray!T && !isAssociativeArray!T)) { @@ -272,30 +274,31 @@ struct Node } } - /// Construct a node from an _array. - /// - /// 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 - /// those nodes are stored. - /// - /// Params: array = Values to store in the node. - /// tag = Overrides tag of the node when emitted, regardless - /// of tag determined by Representer. Representer uses - /// this to determine YAML data type when a D data type - /// maps to multiple different YAML data types. - /// This is used to differentiate between YAML sequences - /// ("!!seq") and sets ("!!set"), which both are - /// internally represented as an array_ of nodes. Tag - /// 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"); - /// -------------------- + /** Construct a node from an _array. + * + * 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 + * those nodes are stored. + * + * Params: array = Values to store in the node. + * tag = Overrides tag of the node when emitted, regardless + * of tag determined by Representer. Representer uses + * this to determine YAML data type when a D data type + * maps to multiple different YAML data types. + * This is used to differentiate between YAML sequences + * ("!!seq") and sets ("!!set"), which both are + * internally represented as an array_ of nodes. Tag + * 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) @safe if (!isSomeString!(T[])) { @@ -333,32 +336,33 @@ struct Node auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set"); } - /// Construct a node from an associative _array. - /// - /// If keys and/or values of _array are nodes, they stored directly. - /// Otherwise they are converted to nodes and then stored. - /// - /// Params: array = Values to store in the node. - /// tag = Overrides tag of the node when emitted, regardless - /// of tag determined by Representer. Representer uses - /// this to determine YAML data type when a D data type - /// maps to multiple different YAML data types. - /// This is used to differentiate between YAML unordered - /// mappings ("!!map"), ordered mappings ("!!omap"), and - /// pairs ("!!pairs") which are all internally - /// represented as an _array of node pairs. Tag must be - /// 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"); - /// -------------------- + /** Construct a node from an associative _array. + * + * If keys and/or values of _array are nodes, they stored directly. + * Otherwise they are converted to nodes and then stored. + * + * Params: array = Values to store in the node. + * tag = Overrides tag of the node when emitted, regardless + * of tag determined by Representer. Representer uses + * this to determine YAML data type when a D data type + * maps to multiple different YAML data types. + * This is used to differentiate between YAML unordered + * mappings ("!!map"), ordered mappings ("!!omap"), and + * pairs ("!!pairs") which are all internally + * represented as an _array of node pairs. Tag must be + * 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) @safe { tag_ = Tag(tag); @@ -387,41 +391,42 @@ struct Node auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs"); } - /// Construct a node from arrays of _keys and _values. - /// - /// Constructs a mapping node with key-value pairs from - /// _keys and _values, keeping their order. Useful when order - /// is important (ordered maps, pairs). - /// - /// - /// keys and values must have equal length. - /// - /// - /// If _keys and/or _values are nodes, they are stored directly/ - /// Otherwise they are converted to nodes and then stored. - /// - /// Params: keys = Keys 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 - /// of tag determined by Representer. Representer uses - /// this to determine YAML data type when a D data type - /// maps to multiple different YAML data types. - /// This is used to differentiate between YAML unordered - /// mappings ("!!map"), ordered mappings ("!!omap"), and - /// pairs ("!!pairs") which are all internally - /// represented as an array of node pairs. Tag must be - /// 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"); - /// -------------------- + /** Construct a node from arrays of _keys and _values. + * + * Constructs a mapping node with key-value pairs from + * _keys and _values, keeping their order. Useful when order + * is important (ordered maps, pairs). + * + * + * keys and values must have equal length. + * + * + * If _keys and/or _values are nodes, they are stored directly/ + * Otherwise they are converted to nodes and then stored. + * + * Params: keys = Keys 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 + * of tag determined by Representer. Representer uses + * this to determine YAML data type when a D data type + * maps to multiple different YAML data types. + * This is used to differentiate between YAML unordered + * mappings ("!!map"), ordered mappings ("!!omap"), and + * pairs ("!!pairs") which are all internally + * represented as an array of node pairs. Tag must be + * 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) @safe if(!(isSomeString!(K[]) || isSomeString!(V[]))) in @@ -494,32 +499,25 @@ struct Node /// Return tag of the node. @property string tag() const @safe nothrow {return tag_.get;} - /// Equality test. - /// - /// If T is Node, recursively compare all subnodes. - /// 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 - /// equality with that. - /// - /// To test equality with a null YAML value, use YAMLNull. - /// - /// Examples: - /// -------------------- - /// auto node = Node(42); - /// - /// assert(node == 42); - /// assert(node != "42"); - /// assert(node != "43"); - /// -------------------- - /// - /// Params: rhs = Variable to test equality with. - /// - /// Returns: true if equal, false otherwise. + /** Equality test. + * + * If T is Node, recursively compares all subnodes. + * This might be quite expensive if testing entire documents. + * + * If T is not Node, gets a value of type T from the node and tests + * equality with that. + * + * To test equality with a null YAML value, use YAMLNull. + * + * Params: rhs = Variable to test equality with. + * + * Returns: true if equal, false otherwise. + */ bool opEquals(T)(const auto ref T rhs) const @safe { return equals!(Yes.useTag)(rhs); } + /// unittest { auto node = Node(42); @@ -535,48 +533,49 @@ struct Node /// Shortcut for get(). alias get as; - /// Get the value of the node as specified type. - /// - /// If the specifed type does not match type in the node, - /// conversion is attempted. The stringConversion template - /// parameter can be used to disable conversion from non-string - /// types to strings. - /// - /// Numeric values are range checked, throwing if out of range of - /// requested type. - /// - /// Timestamps are stored as std.datetime.SysTime. - /// Binary values are decoded and stored as ubyte[]. - /// - /// To get a null value, use get!YAMLNull . This is to - /// prevent getting null values for types such as strings or classes. - /// - /// $(BR)$(B Mapping default values:) - /// - /// $(PBR - /// 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, - /// 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. - /// 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. - /// ) - /// - /// 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 - /// the value is out of range of requested type. + /** Get the value of the node as specified type. + * + * If the specifed type does not match type in the node, + * conversion is attempted. The stringConversion template + * parameter can be used to disable conversion from non-string + * types to strings. + * + * Numeric values are range checked, throwing if out of range of + * requested type. + * + * Timestamps are stored as std.datetime.SysTime. + * Binary values are decoded and stored as ubyte[]. + * + * To get a null value, use get!YAMLNull . This is to + * prevent getting null values for types such as strings or classes. + * + * $(BR)$(B Mapping default values:) + * + * $(PBR + * 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, + * 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. + * 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. + * ) + * + * 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 + * the value is out of range of requested type. + */ @property T get(T, Flag!"stringConversion" stringConversion = Yes.stringConversion)() @trusted if(!is(T == const)) { @@ -711,13 +710,14 @@ struct Node } } - /// If this is a collection, return its _length. - /// - /// Otherwise, throw NodeException. - /// - /// 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. + /** If this is a collection, return its _length. + * + * Otherwise, throw NodeException. + * + * 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. + */ @property size_t length() const @safe { if(isSequence) {return value_.get!(const Node[]).length;} @@ -726,24 +726,25 @@ struct Node startMark_); } - /// Get the element at specified index. - /// - /// If the node is a sequence, index must be integral. - /// - /// - /// 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 - /// has a specific key. - /// - /// To get element at a null index, use YAMLNull for index. - /// - /// Params: index = Index to use. - /// - /// Returns: Value corresponding to the index. - /// - /// Throws: NodeException if the index could not be found, - /// non-integral index is used with a sequence or the node is - /// not a collection. + /** Get the element at specified index. + * + * If the node is a sequence, index must be integral. + * + * + * 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 + * has a specific key. + * + * To get element at a null index, use YAMLNull for index. + * + * Params: index = Index to use. + * + * Returns: Value corresponding to the index. + * + * Throws: NodeException if the index could not be found, + * non-integral index is used with a sequence or the node is + * not a collection. + */ ref Node opIndex(T)(T index) @trusted { if(isSequence) @@ -807,36 +808,31 @@ struct Node assertThrown!NodeException(nmap[14]); } - /// Determine if a collection contains 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. - /// - /// To check for a null value, use YAMLNull for rhs. - /// - /// Params: rhs = Item to look for. - /// - /// Returns: true if rhs was found, false otherwise. - /// - /// Throws: NodeException if the node is not a collection. + /** Determine if a collection contains 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. + * + * Params: rhs = Item to look for. Use YAMLNull to check for a null value. + * + * Returns: true if rhs was found, false otherwise. + * + * Throws: NodeException if the node is not a collection. + */ bool contains(T)(T rhs) const @safe { return contains_!(T, No.key, "contains")(rhs); } - /// Determine if a collection contains specified key. - /// - /// If the node is a mapping, check if it has a key - /// that matches specified key. - /// - /// To check for a null key, use YAMLNull for rhs. - /// - /// Params: rhs = Item to look for. - /// - /// Returns: true if rhs was found, false otherwise. - /// - /// Throws: NodeException if the node is not a mapping. + /** Determine if a mapping contains specified key. + * + * Params: rhs = Key to look for. Use YAMLNull to check for a null key. + * + * Returns: true if rhs was found, false otherwise. + * + * Throws: NodeException if the node is not a mapping. + */ bool containsKey(T)(T rhs) const @safe { return contains_!(T, Yes.key, "containsKey")(rhs); @@ -921,26 +917,27 @@ struct Node "Node.opAssign() doesn't produce an equivalent copy"); } - /// Set element at specified index in a collection. - /// - /// This method can only be called on collection nodes. - /// - /// If the node is a sequence, index must be integral. - /// - /// If the node is a mapping, sets the _value corresponding to the first - /// 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 - /// pair is added to the mapping. In sequences the index must be in - /// range. This ensures behavior siilar to D arrays and associative - /// arrays. - /// - /// To set element at a null index, use YAMLNull for index. - /// - /// Params: 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. + /** Set element at specified index in a collection. + * + * This method can only be called on collection nodes. + * + * If the node is a sequence, index must be integral. + * + * If the node is a mapping, sets the _value corresponding to the first + * 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 + * pair is added to the mapping. In sequences the index must be in + * range. This ensures behavior siilar to D arrays and associative + * arrays. + * + * To set element at a null index, use YAMLNull for index. + * + * Params: 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. + */ void opIndexAssign(K, V)(V value, K index) @safe { if(isSequence()) @@ -1005,13 +1002,14 @@ struct Node } } - /// Foreach over a sequence, getting each element as T. - /// - /// If T is Node, simply iterate over the nodes in the sequence. - /// Otherwise, convert each node to T during iteration. - /// - /// Throws: NodeException if the node is not a sequence or an - /// element could not be converted to specified type. + /** Foreach over a sequence, getting each element as T. + * + * If T is Node, simply iterate over the nodes in the sequence. + * Otherwise, convert each node to T during iteration. + * + * Throws: NodeException if the node is not a sequence or an + * element could not be converted to specified type. + */ int opApply(T)(int delegate(ref T) dg) @trusted { enforce(isSequence, @@ -1060,13 +1058,14 @@ struct Node assert(array2 == [11, 12, 13, 14]); } - /// 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. - /// Otherwise, convert each key/value to T during iteration. - /// - /// Throws: NodeException if the node is not a mapping or an - /// element could not be converted to specified type. + /** 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. + * Otherwise, convert each key/value to T during iteration. + * + * Throws: NodeException if the node is not a mapping or an + * element could not be converted to specified type. + */ int opApply(K, V)(int delegate(ref K, ref V) dg) @trusted { enforce(isMapping, @@ -1152,19 +1151,20 @@ struct Node } } - /// Add an element to a sequence. - /// - /// This method can only be called on sequence nodes. - /// - /// 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. - /// - /// $(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 - /// the sequence are unique, otherwise $(B invalid) YAML code will be - /// emitted.) - /// - /// Params: value = Value to _add to the sequence. + /** Add an element to a sequence. + * + * This method can only be called on sequence nodes. + * + * 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. + * + * $(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 + * the sequence are unique, otherwise $(B invalid) YAML code will be + * emitted.) + * + * Params: value = Value to _add to the sequence. + */ void add(T)(T value) @safe { enforce(isSequence(), @@ -1186,20 +1186,21 @@ struct Node } } - /// Add a key-value pair to a mapping. - /// - /// This method can only be called on mapping nodes. - /// - /// 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. - /// - /// $(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. - /// This is useful with the "!!pairs" tag, but will result in - /// $(B invalid) YAML with "!!map" and "!!omap" tags.) - /// - /// Params: key = Key to _add. - /// value = Value to _add. + /** Add a key-value pair to a mapping. + * + * This method can only be called on mapping nodes. + * + * 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. + * + * $(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. + * This is useful with the "!!pairs" tag, but will result in + * $(B invalid) YAML with "!!map" and "!!omap" tags.) + * + * Params: key = Key to _add. + * value = Value to _add. + */ void add(K, V)(K key, V value) @safe { enforce(isMapping(), @@ -1221,20 +1222,22 @@ struct Node } } - /// Determine whether a key is in a mapping, and access its value. - /// - /// This method can only be called on mapping nodes. - /// - /// Params: key = Key to search for. - /// - /// Returns: A pointer to the value (as a Node) corresponding to key, - /// or null if not found. - /// - /// Note: Any modification to the node can invalidate the returned - /// pointer. - /// - /// See_Also: contains - Node* opBinaryRight(string op, K)(K key) @trusted if (op == "in") + /** Determine whether a key is in a mapping, and access its value. + * + * This method can only be called on mapping nodes. + * + * Params: key = Key to search for. + * + * Returns: A pointer to the value (as a Node) corresponding to key, + * or null if not found. + * + * Note: Any modification to the node can invalidate the returned + * pointer. + * + * See_Also: contains + */ + Node* opBinaryRight(string op, K)(K key) @system + if (op == "in") { enforce(isMapping, new Error("Trying to use 'in' on a " ~ nodeTypeString ~ " node", startMark_)); @@ -1262,17 +1265,18 @@ struct Node assert(mapping["foo"] == Node("newfoo")); } - /// Remove first (if any) occurence of a value in a collection. - /// - /// 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 mapping, the first key-value pair where _value - /// matches specified value is removed. - /// - /// Params: rhs = Value to _remove. - /// - /// Throws: NodeException if the node is not a collection. + /** Remove first (if any) occurence of a value in a collection. + * + * 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 mapping, the first key-value pair where _value + * matches specified value is removed. + * + * Params: rhs = Value to _remove. + * + * Throws: NodeException if the node is not a collection. + */ void remove(T)(T rhs) @trusted { remove_!(T, No.key, "remove")(rhs); @@ -1303,23 +1307,24 @@ struct Node } } - /// Remove element at the specified index of a collection. - /// - /// This method can only be called on collection nodes. - /// - /// If the node is a sequence, index must be integral. - /// - /// If the node is a mapping, remove the first key-value pair where - /// key matches index. - /// - /// 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 associative arrays. - /// - /// Params: index = Index to remove at. - /// - /// 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. + /** Remove element at the specified index of a collection. + * + * This method can only be called on collection nodes. + * + * If the node is a sequence, index must be integral. + * + * If the node is a mapping, remove the first key-value pair where + * key matches index. + * + * 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 associative arrays. + * + * Params: index = Index to remove at. + * + * 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. + */ void removeAt(T)(T index) @trusted { remove_!(T, Yes.key, "removeAt")(index); @@ -1355,26 +1360,15 @@ struct 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(); - // 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. - return tagHash + (cast(Value)value_).toHash(); - } - catch(Exception e) - { - assert(false, "Unexpected exception caught"); - } - } - return unsafeHash(); + const tagHash = tag_.isNull ? 0 : tag_.toHash(); + // Variant toHash is not const at the moment, so we need to const-cast. + return tagHash + value_.toHash(); + } + unittest + { + writeln("Node(42).toHash(): ", Node(42).toHash()); } package: