convert examples to doc'd unittests, fix some parameter errors, remove some redundancy

This commit is contained in:
Cameron Ross 2018-04-07 17:36:38 -03:00
parent 7b316fdffb
commit 9d35b773b4
No known key found for this signature in database
GPG key ID: 777897D98DC91C54
11 changed files with 492 additions and 545 deletions

View file

@ -137,47 +137,6 @@ final class Constructor
* *
* Params: tag = Tag for the function to handle. * Params: tag = Tag for the function to handle.
* ctor = Constructor function. * 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) void addConstructorScalar(T)(const string tag, T function(ref Node) ctor)
{ {
@ -185,49 +144,44 @@ final class Constructor
auto deleg = addConstructor!T(t, ctor); auto deleg = addConstructor!T(t, ctor);
(*delegates!string)[t] = deleg; (*delegates!string)[t] = deleg;
} }
///
@safe unittest
{
static struct MyStruct
{
int x, y, z;
//Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
}
static MyStruct constructMyStructScalar(ref Node node) @safe
{
//Guaranteed to be string as we construct from scalar.
//!mystruct x:y:z
auto parts = node.as!string().split(":");
// If this throws, the D:YAML will handle it and throw a YAMLException.
return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
}
auto loader = Loader.fromString("!mystruct 12:34:56".dup);
auto constructor = new Constructor;
constructor.addConstructorScalar("!mystruct", &constructMyStructScalar);
loader.constructor = constructor;
Node node = loader.load();
assert(node.get!MyStruct == MyStruct(12, 34, 56));
}
/** Add a constructor function from sequence. /** Add a constructor function from sequence.
* *
* See_Also: addConstructorScalar * 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) void addConstructorSequence(T)(const string tag, T function(ref Node) ctor)
{ {
@ -235,49 +189,40 @@ final class Constructor
auto deleg = addConstructor!T(t, ctor); auto deleg = addConstructor!T(t, ctor);
(*delegates!(Node[]))[t] = deleg; (*delegates!(Node[]))[t] = deleg;
} }
///
@safe unittest {
static struct MyStruct
{
int x, y, z;
//Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
}
static MyStruct constructMyStructSequence(ref Node node) @safe
{
//node is guaranteed to be sequence.
//!mystruct [x, y, z]
return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
}
auto loader = Loader.fromString("!mystruct [1,2,3]".dup);
auto constructor = new Constructor;
constructor.addConstructorSequence("!mystruct", &constructMyStructSequence);
loader.constructor = constructor;
Node node = loader.load();
assert(node.get!MyStruct == MyStruct(1, 2, 3));
}
/** Add a constructor function from a mapping. /** Add a constructor function from a mapping.
* *
* See_Also: addConstructorScalar * 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) void addConstructorMapping(T)(const string tag, T function(ref Node) ctor)
{ {
@ -285,6 +230,36 @@ final class Constructor
auto deleg = addConstructor!T(t, ctor); auto deleg = addConstructor!T(t, ctor);
(*delegates!(Node.Pair[]))[t] = deleg; (*delegates!(Node.Pair[]))[t] = deleg;
} }
///
@safe unittest {
static struct MyStruct
{
int x, y, z;
//Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
}
static MyStruct constructMyStructMapping(ref Node node) @safe
{
//node is guaranteed to be mapping.
//!mystruct {"x": x, "y": y, "z": z}
return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
}
auto loader = Loader.fromString(`!mystruct {"x": 11, "y": 22, "z": 33}`.dup);
auto constructor = new Constructor;
constructor.addConstructorMapping("!mystruct", &constructMyStructMapping);
loader.constructor = constructor;
Node node = loader.load();
assert(node.get!MyStruct == MyStruct(11, 22, 33));
}
package: package:
/* /*

View file

@ -31,52 +31,10 @@ import dyaml.tagdirective;
/** /**
* Dumps YAML documents to files or streams. * Dumps YAML documents to files or streams.
* *
* User specified Representer and/or Resolver can be used to support new * User specified Representer and/or Resolver can be used to support new
* tags / data types. * tags / data types.
* *
* Setters are provided to affect output details (style, encoding, etc.). * Setters are provided to affect output details (style, encoding, etc.).
*
* Examples:
*
* Write to a file:
* --------------------
* auto node = Node([1, 2, 3, 4, 5]);
* Dumper("file.yaml").dump(node);
* --------------------
*
* Write multiple YAML documents to a file:
* --------------------
* auto node1 = Node([1, 2, 3, 4, 5]);
* auto node2 = Node("This document contains only one string");
* Dumper("file.yaml").dump(node1, node2);
*
* //Or with an array:
* //Dumper("file.yaml").dump([node1, node2]);
*
*
* --------------------
*
* Write to memory:
* --------------------
* import std.stream;
* auto stream = new YMemoryStream();
* auto node = Node([1, 2, 3, 4, 5]);
* Dumper(stream).dump(node);
* --------------------
*
* Use a custom representer/resolver to support custom data types and/or implicit tags:
* --------------------
* auto node = Node([1, 2, 3, 4, 5]);
* auto representer = new Representer();
* auto resolver = new Resolver();
*
* //Add representer functions / resolver expressions here...
*
* auto dumper = Dumper("file.yaml");
* dumper.representer = representer;
* dumper.resolver = resolver;
* dumper.dump(node);
* --------------------
*/ */
struct Dumper struct Dumper
{ {
@ -271,21 +229,10 @@ struct Dumper
* *
* Each prefix MUST not be empty. * Each prefix MUST not be empty.
* *
* The "!!" handle is used for default YAML _tags with prefix * The "!!" handle is used for default YAML _tags with prefix
* "tag:yaml.org,2002:". This can be overridden. * "tag:yaml.org,2002:". This can be overridden.
* *
* Params: tags = Tag directives (keys are handles, values are prefixes). * Params: tags = Tag directives (keys are handles, values are prefixes).
*
* Example:
* --------------------
* Dumper dumper = Dumper("file.yaml");
* string[string] directives;
* directives["!short!"] = "tag:long.org,2011:";
* //This will emit tags starting with "tag:long.org,2011"
* //with a "!short!" prefix instead.
* dumper.tagDirectives(directives);
* dumper.dump(Node("foo"));
* --------------------
*/ */
@property void tagDirectives(string[string] tags) pure @safe @property void tagDirectives(string[string] tags) pure @safe
{ {
@ -300,6 +247,17 @@ struct Dumper
} }
tags_ = t; tags_ = t;
} }
///
@safe unittest
{
Dumper dumper = Dumper("example.yaml");
string[string] directives;
directives["!short!"] = "tag:long.org,2011:";
//This will emit tags starting with "tag:long.org,2011"
//with a "!short!" prefix instead.
dumper.tagDirectives(directives);
dumper.dump(Node("foo"));
}
/** /**
* Dump one or more YAML _documents to the file/stream. * Dump one or more YAML _documents to the file/stream.
@ -357,3 +315,38 @@ struct Dumper
} }
} }
} }
///Write to a file
@safe unittest
{
auto node = Node([1, 2, 3, 4, 5]);
Dumper("example.yaml").dump(node);
}
///Write multiple YAML documents to a file
@safe unittest
{
auto node1 = Node([1, 2, 3, 4, 5]);
auto node2 = Node("This document contains only one string");
Dumper("example.yaml").dump(node1, node2);
//Or with an array:
Dumper("example.yaml").dump([node1, node2]);
}
///Write to memory
@safe unittest
{
import dyaml.stream;
auto stream = new YMemoryStream();
auto node = Node([1, 2, 3, 4, 5]);
Dumper(stream).dump(node);
}
///Use a custom representer/resolver to support custom data types and/or implicit tags
@safe unittest
{
auto node = Node([1, 2, 3, 4, 5]);
auto representer = new Representer();
auto resolver = new Resolver();
//Add representer functions / resolver expressions here...
auto dumper = Dumper("example.yaml");
dumper.representer = representer;
dumper.resolver = resolver;
dumper.dump(node);
}

View file

@ -123,6 +123,7 @@ Event event(EventID id)(const Mark start, const Mark end, const string anchor =
* anchor = Anchor of the sequence, if any. * anchor = Anchor of the sequence, if any.
* tag = Tag of the sequence, if specified. * tag = Tag of the sequence, if specified.
* implicit = Should the tag be implicitly resolved? * implicit = Should the tag be implicitly resolved?
* style = Style to use when outputting document.
*/ */
Event collectionStartEvent(EventID id) Event collectionStartEvent(EventID id)
(const Mark start, const Mark end, const string anchor, const string tag, (const Mark start, const Mark end, const string anchor, const string tag,

View file

@ -17,20 +17,6 @@ package:
* Struct holding multiple named boolean values in a single byte. * Struct holding multiple named boolean values in a single byte.
* *
* Can hold at most 8 values. * Can hold at most 8 values.
*
* Example:
* --------------------
* Flags!("empty", "multiline") flags;
* assert(flags.empty == false && flags.multiline == false);
* flags.multiline = true;
* assert(flags.empty == false && flags.multiline == true);
* flags.empty = true;
* assert(flags.empty == true && flags.multiline == true);
* flags.multiline = false;
* assert(flags.empty == true && flags.multiline == false);
* flags.empty = false;
* assert(flags.empty == false && flags.multiline == false);
* --------------------
*/ */
struct Flags(names ...) if(names.length <= 8) struct Flags(names ...) if(names.length <= 8)
{ {
@ -72,6 +58,7 @@ struct Flags(names ...) if(names.length <= 8)
///Flag accessors. ///Flag accessors.
mixin(flags(names)); mixin(flags(names));
} }
///
@safe unittest @safe unittest
{ {
import std.stdio; import std.stdio;

View file

@ -23,22 +23,22 @@ import dyaml.style;
* 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:
* --------------------
* // Node node // loaded from a file
* if(node.isScalar)
* {
* import std.stdio;
* 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");
return node.scalarStyle; return node.scalarStyle;
} }
///
@safe unittest
{
import dyaml;
Node node = Loader.fromString(`"42"`.dup).load(); // loaded from a file
if(node.isScalar)
{
assert(node.scalarStyleHack() == ScalarStyle.DoubleQuoted);
}
}
@safe unittest @safe unittest
{ {
writeln("D:YAML scalarStyleHack getter unittest"); writeln("D:YAML scalarStyleHack getter unittest");

View file

@ -28,74 +28,6 @@ import dyaml.token;
* *
* 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:
*
* 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 struct Loader
{ {
@ -375,7 +307,48 @@ struct Loader
if(constructor_ is null) { constructor_ = new Constructor(); } if(constructor_ is null) { constructor_ = new Constructor(); }
} }
} }
/// Load single YAML document from a file:
@safe unittest
{
write("example.yaml", "Hello world!");
auto rootNode = Loader("example.yaml").load();
assert(rootNode == "Hello world!");
}
/// Load all YAML documents from a file:
@safe unittest
{
import std.file : write;
write("example.yaml",
"---\n"~
"Hello world!\n"~
"...\n"~
"---\n"~
"Hello world 2!\n"~
"...\n"
);
auto nodes = Loader("example.yaml").loadAll();
assert(nodes.length == 2);
}
/// Iterate over YAML documents in a file, lazily loading them:
@safe unittest
{
import std.file : write;
write("example.yaml",
"---\n"~
"Hello world!\n"~
"...\n"~
"---\n"~
"Hello world 2!\n"~
"...\n"
);
auto loader = Loader("example.yaml");
foreach(ref node; loader)
{
//Do something
}
}
/// Load YAML from a string:
@safe unittest @safe unittest
{ {
char[] yaml_input = ("red: '#ff0000'\n" ~ char[] yaml_input = ("red: '#ff0000'\n" ~
@ -390,3 +363,50 @@ struct Loader
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:
@safe unittest
{
import std.file : read, write;
import std.stdio : writeln;
// Create a yaml document
write("example.yaml",
"---\n"~
"Hello world!\n"~
"...\n"~
"---\n"~
"Hello world 2!\n"~
"...\n"
);
try
{
void[] buffer = read("example.yaml");
auto yamlNode = Loader(buffer);
// Read data from yamlNode here...
}
catch(FileException e)
{
writeln("Failed to read file 'example.yaml'");
}
}
/// Use a custom constructor/resolver to support custom data types and/or implicit tags:
@safe unittest
{
import std.file : write;
// Create a yaml document
write("example.yaml",
"---\n"~
"Hello world!\n"~
"...\n"
);
auto constructor = new Constructor();
auto resolver = new Resolver();
// Add constructor functions / resolver expressions here...
auto loader = Loader("example.yaml");
loader.constructor = constructor;
loader.resolver = resolver;
auto rootNode = loader.load();
}

View file

@ -311,13 +311,6 @@ struct Node
* 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:
* --------------------
* // Will be emitted as a sequence (default for arrays)
* auto seq = Node([1, 2, 3, 4, 5]);
* // Will be emitted as a set (overriden tag)
* auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set");
* --------------------
*/ */
this(T)(T[] array, const string tag = null) @trusted this(T)(T[] array, const string tag = null) @trusted
if (!isSomeString!(T[])) if (!isSomeString!(T[]))
@ -341,6 +334,14 @@ struct Node
value_ = Value(nodes); value_ = Value(nodes);
} }
} }
///
@safe unittest
{
// Will be emitted as a sequence (default for arrays)
auto seq = Node([1, 2, 3, 4, 5]);
// Will be emitted as a set (overriden tag)
auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set");
}
@safe unittest @safe unittest
{ {
with(Node([1, 2, 3])) with(Node([1, 2, 3]))
@ -350,10 +351,6 @@ struct Node
assert(opIndex(2).as!int == 3); assert(opIndex(2).as!int == 3);
} }
// Will be emitted as a sequence (default for arrays)
auto seq = Node([1, 2, 3, 4, 5]);
// Will be emitted as a set (overriden tag)
auto set = Node([1, 2, 3, 4, 5], "tag:yaml.org,2002:set");
} }
/** Construct a node from an associative _array. /** Construct a node from an associative _array.
@ -373,15 +370,6 @@ struct Node
* in full form, e.g. "tag:yaml.org,2002:omap", not a * in full form, e.g. "tag:yaml.org,2002:omap", not a
* shortcut, like "!!omap". * shortcut, like "!!omap".
* *
* Examples:
* --------------------
* // Will be emitted as an unordered mapping (default for mappings)
* auto map = Node([1 : "a", 2 : "b"]);
* // Will be emitted as an ordered map (overriden tag)
* auto omap = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:omap");
* // Will be emitted as pairs (overriden tag)
* auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs");
* --------------------
*/ */
this(K, V)(V[K] array, const string tag = null) @trusted this(K, V)(V[K] array, const string tag = null) @trusted
{ {
@ -391,6 +379,16 @@ struct Node
foreach(key, ref value; array){pairs ~= Pair(key, value);} foreach(key, ref value; array){pairs ~= Pair(key, value);}
value_ = Value(pairs); value_ = Value(pairs);
} }
///
@safe unittest
{
// Will be emitted as an unordered mapping (default for mappings)
auto map = Node([1 : "a", 2 : "b"]);
// Will be emitted as an ordered map (overriden tag)
auto omap = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:omap");
// Will be emitted as pairs (overriden tag)
auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs");
}
@safe unittest @safe unittest
{ {
int[string] aa; int[string] aa;
@ -403,12 +401,6 @@ struct Node
assert(opIndex("2").as!int == 2); assert(opIndex("2").as!int == 2);
} }
// Will be emitted as an unordered mapping (default for mappings)
auto map = Node([1 : "a", 2 : "b"]);
// Will be emitted as an ordered map (overriden tag)
auto omap = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:omap");
// Will be emitted as pairs (overriden tag)
auto pairs = Node([1 : "a", 2 : "b"], "tag:yaml.org,2002:pairs");
} }
/** Construct a node from arrays of _keys and _values. /** Construct a node from arrays of _keys and _values.
@ -437,15 +429,6 @@ struct Node
* in full form, e.g. "tag:yaml.org,2002:omap", not a * in full form, e.g. "tag:yaml.org,2002:omap", not a
* shortcut, like "!!omap". * shortcut, like "!!omap".
* *
* Examples:
* --------------------
* // Will be emitted as an unordered mapping (default for mappings)
* auto map = Node([1, 2], ["a", "b"]);
* // Will be emitted as an ordered map (overriden tag)
* auto omap = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:omap");
* // Will be emitted as pairs (overriden tag)
* auto pairs = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:pairs");
* --------------------
*/ */
this(K, V)(K[] keys, V[] values, const string tag = null) @trusted this(K, V)(K[] keys, V[] values, const string tag = null) @trusted
if(!(isSomeString!(K[]) || isSomeString!(V[]))) if(!(isSomeString!(K[]) || isSomeString!(V[])))
@ -463,6 +446,16 @@ struct Node
foreach(i; 0 .. keys.length){pairs ~= Pair(keys[i], values[i]);} foreach(i; 0 .. keys.length){pairs ~= Pair(keys[i], values[i]);}
value_ = Value(pairs); value_ = Value(pairs);
} }
///
@safe unittest
{
// Will be emitted as an unordered mapping (default for mappings)
auto map = Node([1, 2], ["a", "b"]);
// Will be emitted as an ordered map (overridden tag)
auto omap = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:omap");
// Will be emitted as pairs (overriden tag)
auto pairs = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:pairs");
}
@safe unittest @safe unittest
{ {
with(Node(["1", "2"], [1, 2])) with(Node(["1", "2"], [1, 2]))
@ -472,12 +465,6 @@ struct Node
assert(opIndex("2").as!int == 2); assert(opIndex("2").as!int == 2);
} }
// Will be emitted as an unordered mapping (default for mappings)
auto map = Node([1, 2], ["a", "b"]);
// Will be emitted as an ordered map (overriden tag)
auto omap = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:omap");
// Will be emitted as pairs (overriden tag)
auto pairs = Node([1, 2], ["a", "b"], "tag:yaml.org,2002:pairs");
} }
/// Is this node valid (initialized)? /// Is this node valid (initialized)?
@ -580,17 +567,6 @@ struct Node
* by old versions of the program, which expect the node to be a scalar. * 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. * 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
@ -669,6 +645,15 @@ struct Node
} }
assert(false, "This code should never be reached"); assert(false, "This code should never be reached");
} }
/// Automatic type conversion
@safe unittest
{
auto node = Node(42);
assert(node.get!int == 42);
assert(node.get!string == "42");
assert(node.get!double == 42.0);
}
@safe unittest @safe unittest
{ {
assertThrown!NodeException(Node("42").get!int); assertThrown!NodeException(Node("42").get!int);
@ -968,7 +953,9 @@ struct Node
* *
* 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:
* value = Value to assign.
* 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.

View file

@ -363,17 +363,7 @@ auto decodeUTF8NoGC(Flag!"validated" validated)(const(char[]) str, ref size_t in
else { return invalidUTF; } else { return invalidUTF; }
} }
/// @nogc version of std.utf.decode() for char[], but assumes str is valid UTF-8. /// ditto
///
/// The caller $(B must) handle ASCII (< 0x80) characters manually; this is asserted to
/// force code using this function to be efficient.
///
/// Params:
///
/// str = Will decode the first code point from this string. Must be valid UTF-8,
/// otherwise undefined behavior WILL occur.
/// index = Index in str where the code point starts. Will be updated to point to the
/// next code point.
alias decodeValidUTF8NoGC = decodeUTF8NoGC!(Yes.validated); alias decodeValidUTF8NoGC = decodeUTF8NoGC!(Yes.validated);
/// @nogc version of std.utf.encode() for char[]. /// @nogc version of std.utf.encode() for char[].
@ -451,18 +441,7 @@ auto encodeCharNoGC(Flag!"validated" validated)(ref char[4] buf, dchar c)
} }
} }
/// @nogc version of std.utf.encode() for char[], but assumes c is a valid UTF-32 char. /// ditto
///
/// The caller $(B must) handle ASCII (< 0x80) characters manually; this is asserted to
/// force code using this function to be efficient.
///
/// Params:
///
/// buf = Buffer to write the encoded result to.
/// c = Character to encode. Must be valid UTF-32, otherwise undefined behavior
/// $(D will) occur.
///
/// Returns: Number of bytes the encoded character takes up in buf.
alias encodeValidCharNoGC = encodeCharNoGC!(Yes.validated); alias encodeValidCharNoGC = encodeCharNoGC!(Yes.validated);
/// @nogc version of std.utf.isValidDchar /// @nogc version of std.utf.isValidDchar

View file

@ -108,7 +108,8 @@ final class Representer
* throw a $(D RepresenterException). See the example for more information. * throw a $(D RepresenterException). See the example for more information.
* *
* *
* Only one function may be specified for one data type. Default data *
* Only one function may be specified for one data type. Default data
* types already have representer functions unless disabled in the * types already have representer functions unless disabled in the
* $(D Representer) constructor. * $(D Representer) constructor.
* *
@ -121,106 +122,6 @@ final class Representer
* values - it is not const for compatibility reasons. * values - it is not const for compatibility reasons.
* *
* Params: representer = Representer function to add. * Params: representer = Representer function to add.
*
* Examples:
*
* Representing a simple struct:
* --------------------
* import std.string;
*
* import dyaml.all;
*
* struct MyStruct
* {
* int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* }
*
* Node representMyStruct(ref Node node, Representer representer)
* {
* //The node is guaranteed to be MyStruct as we add representer for MyStruct.
* auto value = node.as!MyStruct;
* //Using custom scalar format, x:y:z.
* auto scalar = format("%s:%s:%s", value.x, value.y, value.z);
* //Representing as a scalar, with custom tag to specify this data type.
* return representer.representScalar("!mystruct.tag", scalar);
* }
*
* void main()
* {
* auto dumper = Dumper("file.yaml");
* auto representer = new Representer;
* representer.addRepresenter!MyStruct(&representMyStruct);
* dumper.representer = representer;
* dumper.dump(Node(MyStruct(1,2,3)));
* }
* --------------------
*
* Representing a class:
* --------------------
* import std.string;
*
* import dyaml.all;
*
* class MyClass
* {
* int x, y, z;
*
* this(int x, int y, int z)
* {
* this.x = x;
* this.y = y;
* this.z = z;
* }
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* override int opCmp(Object o)
* {
* MyClass s = cast(MyClass)o;
* if(s is null){return -1;}
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
*
* ///Useful for Node.as!string .
* override string toString()
* {
* return format("MyClass(%s, %s, %s)", x, y, z);
* }
* }
*
* //Same as representMyStruct.
* Node representMyClass(ref Node node, Representer representer)
* {
* //The node is guaranteed to be MyClass as we add representer for MyClass.
* auto value = node.as!MyClass;
* //Using custom scalar format, x:y:z.
* auto scalar = format("%s:%s:%s", value.x, value.y, value.z);
* //Representing as a scalar, with custom tag to specify this data type.
* return representer.representScalar("!myclass.tag", scalar);
* }
*
* void main()
* {
* auto dumper = Dumper("file.yaml");
* auto representer = new Representer;
* representer.addRepresenter!MyClass(&representMyClass);
* dumper.representer = representer;
* dumper.dump(Node(new MyClass(1,2,3)));
* }
* --------------------
*/ */
void addRepresenter(T)(Node function(ref Node, Representer) @safe representer) void addRepresenter(T)(Node function(ref Node, Representer) @safe representer)
@safe pure @safe pure
@ -230,6 +131,96 @@ final class Representer
" already specified. Can't specify another one"); " already specified. Can't specify another one");
representers_[typeid(T)] = representer; representers_[typeid(T)] = representer;
} }
/// Representing a simple struct:
unittest {
import std.string;
import dyaml;
struct MyStruct
{
int x, y, z;
//Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
}
static Node representMyStruct(ref Node node, Representer representer) @safe
{
//The node is guaranteed to be MyStruct as we add representer for MyStruct.
auto value = node.as!MyStruct;
//Using custom scalar format, x:y:z.
auto scalar = format("%s:%s:%s", value.x, value.y, value.z);
//Representing as a scalar, with custom tag to specify this data type.
return representer.representScalar("!mystruct.tag", scalar);
}
auto dumper = Dumper("example.yaml");
auto representer = new Representer;
representer.addRepresenter!MyStruct(&representMyStruct);
dumper.representer = representer;
dumper.dump(Node(MyStruct(1,2,3)));
}
/// Representing a class:
unittest {
import std.string;
import dyaml;
class MyClass
{
int x, y, z;
this(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
//Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
override int opCmp(Object o)
{
MyClass s = cast(MyClass)o;
if(s is null){return -1;}
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
///Useful for Node.as!string .
override string toString()
{
return format("MyClass(%s, %s, %s)", x, y, z);
}
}
//Same as representMyStruct.
static Node representMyClass(ref Node node, Representer representer) @safe
{
//The node is guaranteed to be MyClass as we add representer for MyClass.
auto value = node.as!MyClass;
//Using custom scalar format, x:y:z.
auto scalar = format("%s:%s:%s", value.x, value.y, value.z);
//Representing as a scalar, with custom tag to specify this data type.
return representer.representScalar("!myclass.tag", scalar);
}
auto dumper = Dumper("example.yaml");
auto representer = new Representer;
representer.addRepresenter!MyClass(&representMyClass);
dumper.representer = representer;
dumper.dump(Node(new MyClass(1,2,3)));
}
//If profiling shows a bottleneck on tag construction in these 3 methods, //If profiling shows a bottleneck on tag construction in these 3 methods,
//we'll need to take Tag directly and have string based wrappers for //we'll need to take Tag directly and have string based wrappers for
@ -246,31 +237,6 @@ final class Representer
* If the node was loaded before, previous _style will always be used. * If the node was loaded before, previous _style will always be used.
* *
* Returns: The represented node. * Returns: The represented node.
*
* Example:
* --------------------
* struct MyStruct
* {
* int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* }
*
* Node representMyStruct(ref Node node, Representer representer)
* {
* auto value = node.as!MyStruct;
* auto scalar = format("%s:%s:%s", value.x, value.y, value.z);
* return representer.representScalar("!mystruct.tag", scalar);
* }
* --------------------
*/ */
Node representScalar(string tag, string scalar, Node representScalar(string tag, string scalar,
ScalarStyle style = ScalarStyle.Invalid) @trusted ScalarStyle style = ScalarStyle.Invalid) @trusted
@ -279,6 +245,37 @@ final class Representer
return Node.rawNode(Node.Value(scalar), Mark(), tag, style, return Node.rawNode(Node.Value(scalar), Mark(), tag, style,
CollectionStyle.Invalid); CollectionStyle.Invalid);
} }
///
@safe unittest
{
struct MyStruct
{
int x, y, z;
//Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
}
static Node representMyStruct(ref Node node, Representer representer)
{
auto value = node.as!MyStruct;
auto scalar = format("%s:%s:%s", value.x, value.y, value.z);
return representer.representScalar("!mystruct.tag", scalar);
}
auto dumper = Dumper("example.yaml");
auto representer = new Representer;
representer.addRepresenter!MyStruct(&representMyStruct);
dumper.representer = representer;
dumper.dump(Node(MyStruct(1,2,3)));
}
/** /**
* Represent a _sequence with specified _tag, representing children first. * Represent a _sequence with specified _tag, representing children first.
@ -293,33 +290,6 @@ final class Representer
* Returns: The represented node. * Returns: The represented node.
* *
* Throws: $(D RepresenterException) if a child could not be represented. * Throws: $(D RepresenterException) if a child could not be represented.
*
* Example:
* --------------------
* struct MyStruct
* {
* int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* }
*
* Node representMyStruct(ref Node node, Representer representer)
* {
* auto value = node.as!MyStruct;
* auto nodes = [Node(value.x), Node(value.y), Node(value.z)];
* //use flow style
* return representer.representSequence("!mystruct.tag", nodes,
* CollectionStyle.Flow);
* }
* --------------------
*/ */
Node representSequence(string tag, Node[] sequence, Node representSequence(string tag, Node[] sequence,
CollectionStyle style = CollectionStyle.Invalid) @trusted CollectionStyle style = CollectionStyle.Invalid) @trusted
@ -341,14 +311,46 @@ final class Representer
if(style == CollectionStyle.Invalid) if(style == CollectionStyle.Invalid)
{ {
style = defaultCollectionStyle_ != CollectionStyle.Invalid style = defaultCollectionStyle_ != CollectionStyle.Invalid
? defaultCollectionStyle_ ? defaultCollectionStyle_
: bestStyle; : bestStyle;
} }
return Node.rawNode(Node.Value(value), Mark(), tag, return Node.rawNode(Node.Value(value), Mark(), tag,
ScalarStyle.Invalid, style); ScalarStyle.Invalid, style);
} }
///
@safe unittest
{
struct MyStruct
{
int x, y, z;
//Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
}
static Node representMyStruct(ref Node node, Representer representer)
{
auto value = node.as!MyStruct;
auto nodes = [Node(value.x), Node(value.y), Node(value.z)];
//use flow style
return representer.representSequence("!mystruct.tag", nodes,
CollectionStyle.Flow);
}
auto dumper = Dumper("example.yaml");
auto representer = new Representer;
representer.addRepresenter!MyStruct(&representMyStruct);
dumper.representer = representer;
dumper.dump(Node(MyStruct(1,2,3)));
}
/** /**
* Represent a mapping with specified _tag, representing children first. * Represent a mapping with specified _tag, representing children first.
* *
@ -362,33 +364,6 @@ final class Representer
* Returns: The represented node. * Returns: The represented node.
* *
* Throws: $(D RepresenterException) if a child could not be represented. * Throws: $(D RepresenterException) if a child could not be represented.
*
* Example:
* --------------------
* struct MyStruct
* {
* int x, y, z;
*
* //Any D:YAML type must have a custom opCmp operator.
* //This is used for ordering in mappings.
* const int opCmp(ref const MyStruct s)
* {
* if(x != s.x){return x - s.x;}
* if(y != s.y){return y - s.y;}
* if(z != s.z){return z - s.z;}
* return 0;
* }
* }
*
* Node representMyStruct(ref Node node, Representer representer)
* {
* auto value = node.as!MyStruct;
* auto pairs = [Node.Pair("x", value.x),
* Node.Pair("y", value.y),
* Node.Pair("z", value.z)];
* return representer.representMapping("!mystruct.tag", pairs);
* }
* --------------------
*/ */
Node representMapping(string tag, Node.Pair[] pairs, Node representMapping(string tag, Node.Pair[] pairs,
CollectionStyle style = CollectionStyle.Invalid) @trusted CollectionStyle style = CollectionStyle.Invalid) @trusted
@ -425,6 +400,39 @@ final class Representer
return Node.rawNode(Node.Value(value), Mark(), tag, return Node.rawNode(Node.Value(value), Mark(), tag,
ScalarStyle.Invalid, style); ScalarStyle.Invalid, style);
} }
///
@safe unittest
{
struct MyStruct
{
int x, y, z;
//Any D:YAML type must have a custom opCmp operator.
//This is used for ordering in mappings.
const int opCmp(ref const MyStruct s)
{
if(x != s.x){return x - s.x;}
if(y != s.y){return y - s.y;}
if(z != s.z){return z - s.z;}
return 0;
}
}
static Node representMyStruct(ref Node node, Representer representer)
{
auto value = node.as!MyStruct;
auto pairs = [Node.Pair("x", value.x),
Node.Pair("y", value.y),
Node.Pair("z", value.z)];
return representer.representMapping("!mystruct.tag", pairs);
}
auto dumper = Dumper("example.yaml");
auto representer = new Representer;
representer.addRepresenter!MyStruct(&representMyStruct);
dumper.representer = representer;
dumper.dump(Node(MyStruct(1,2,3)));
}
package: package:
//Represent a node based on its type, and return the represented result. //Represent a node based on its type, and return the represented result.

View file

@ -92,28 +92,6 @@ final class Resolver
* regexp = Regular expression the scalar must match to have this _tag. * regexp = Regular expression the scalar must match to have this _tag.
* first = String of possible starting characters of the scalar. * first = String of possible starting characters of the scalar.
* *
* Examples:
*
* Resolve scalars starting with 'A' to !_tag :
* --------------------
* import std.regex;
*
* import dyaml.all;
*
* void main()
* {
* auto loader = Loader("file.txt");
* auto resolver = new Resolver();
* resolver.addImplicitResolver("!tag", std.regex.regex("A.*"), "A");
* loader.resolver = resolver;
*
* //Note that we have no constructor from tag "!tag", so we can't
* //actually load anything that resolves to this tag.
* //See Constructor API documentation and tutorial for more information.
*
* auto node = loader.load();
* }
* --------------------
*/ */
void addImplicitResolver(string tag, Regex!char regexp, string first) void addImplicitResolver(string tag, Regex!char regexp, string first)
pure @safe pure @safe
@ -127,6 +105,24 @@ final class Resolver
yamlImplicitResolvers_[c] ~= tuple(tag, regexp); yamlImplicitResolvers_[c] ~= tuple(tag, regexp);
} }
} }
/// Resolve scalars starting with 'A' to !_tag
unittest {
import std.regex;
import dyaml;
auto loader = Loader("example.yaml");
auto resolver = new Resolver();
resolver.addImplicitResolver("!tag", regex("A.*"), "A");
loader.resolver = resolver;
//Note that we have no constructor from tag "!tag", so we can't
//actually load anything that resolves to this tag.
//See Constructor API documentation and tutorial for more information.
//auto node = loader.load();
}
package: package:
/* /*

View file

@ -103,6 +103,7 @@ static assert(Token.sizeof <= 32, "Token has unexpected size");
/// end = End position of the token. /// end = End position of the token.
/// value = Value of the token. /// value = Value of the token.
/// directive = Directive type (YAML or TAG in YAML 1.1). /// directive = Directive type (YAML or TAG in YAML 1.1).
/// nameEnd = Position of the end of the name
Token directiveToken(const Mark start, const Mark end, char[] value, Token directiveToken(const Mark start, const Mark end, char[] value,
DirectiveType directive, const uint nameEnd) @safe pure nothrow @nogc DirectiveType directive, const uint nameEnd) @safe pure nothrow @nogc
{ {