10203040506070809010011012013014015016017018019020021022023024025026027028629630031032233034035143614370380394400410420430440450460470480490500510520530540550560570582459060896361062063064065066376706813669070071072073920749207507607789778679080081082083238414850860870880892900910920930949009509609768098129901006681016681020103010410210501069310701082510901100111900112511301145115511601170118900119112011211122012301249001250126012701280129013011129131013216513301349492135013601370138013901403141314201430144014501460147014801490150541510152621530154813815501560157015801590160016101620163016431650166116711680169217021712172017301740175017601770178017931800181118211830184118501862187218821890190019101920193019401950196419701981199120002011202120312040205220622072208220922102211021202130214021502160217021802193220022112221223022422252226222702280229023002310232023302343235023612371238023912400241224222432244024502460247024802490250025142520253125412550256125712581259026022612262226322642265226602670268026902701271127212730274227522762277027802790280028112821283128402852286228722880289029002910292129312941295029622972298229903000301030203031304130513060307230823092310031103120313031413151316131703182319232023210322032303240325132613271328032923302331233203330334033503361337133813390340234123422343034403450346034713481349135003512352235323540355035603570358035903600361036213631364136503662367236823690370037103720373037413751376137703782379238023810382038303840385038603870388038903900391039203933633940395580539603970398039904000401040204030404040504060407040804090410041104120413041404150416041704180419042004210422042304240425042622542714280429043022443114320433043433343511343604370438110439110440044111044211044311044411044504464959447154344838474490450276845113945213945313945404550456279645710074581545915460046104621007463154640465154661046704680469047004711404472267647312647404750476230347712478047904800481140448204830484110485107486048704881104890490049104920493149404952496049704980499050015010502250305040505050605070508150905102511051205130514051515160517251805190520052105221523052425250526052705280529153005312532053305340535053615370538253905400541054205431544054525460547054805490550155105522553055405550556055715580559256005610562056305641565056625670568056905700571157205732574057505760577057815790580258105820583058405851586258705880589059005911592259305940595059605971598259906000601060206031604260506060607060806091610261106120613061406151538616806170618061914586201458621062221326236076240625062606278516280629063006310632263306340635063606372638063906400641064226430644064506460647264806490650065106525426530654065506560657165806592660066106620663066426650 module fluentasserts.core.serializers; import std.array; import std.string; import std.algorithm; import std.traits; import std.conv; import std.datetime; import std.functional; version(unittest) import fluent.asserts; /// Singleton used to serialize to string the tested values class SerializerRegistry { static SerializerRegistry instance; private { string delegate(void*)[string] serializers; string delegate(const void*)[string] constSerializers; string delegate(immutable void*)[string] immutableSerializers; } /// void register(T)(string delegate(T) serializer) if(isAggregateType!T) { enum key = T.stringof; static if(is(Unqual!T == T)) { string wrap(void* val) { auto value = (cast(T*) val); return serializer(*value); } serializers[key] = &wrap; } else static if(is(ConstOf!T == T)) { string wrap(const void* val) { auto value = (cast(T*) val); return serializer(*value); } constSerializers[key] = &wrap; } else static if(is(ImmutableOf!T == T)) { string wrap(immutable void* val) { auto value = (cast(T*) val); return serializer(*value); } immutableSerializers[key] = &wrap; } } void register(T)(string function(T) serializer) { auto serializerDelegate = serializer.toDelegate; this.register(serializerDelegate); } /// string serialize(T)(T[] value) if(!isSomeString!(T[])) { static if(is(Unqual!T == void)) { return "[]"; } else { return "[" ~ value.map!(a => serialize(a)).joiner(", ").array.to!string ~ "]"; } } /// string serialize(T: V[K], V, K)(T value) { auto keys = value.byKey.array.sort; return "[" ~ keys.map!(a => serialize(a) ~ ":" ~ serialize(value[a])).joiner(", ").array.to!string ~ "]"; } /// string serialize(T)(T value) if(isAggregateType!T) { auto key = T.stringof; auto tmp = &value; static if(is(Unqual!T == T)) { if(key in serializers) { return serializers[key](tmp); } } static if(is(ConstOf!T == T)) { if(key in constSerializers) { return constSerializers[key](tmp); } } static if(is(ImmutableOf!T == T)) { if(key in immutableSerializers) { return immutableSerializers[key](tmp); } } string result; static if(is(T == class)) { if(value is null) { result = "null"; } else { auto v = (cast() value); result = T.stringof ~ "(" ~ v.toHash.to!string ~ ")"; } } else static if(is(Unqual!T == Duration)) { result = value.total!"nsecs".to!string; } else static if(is(Unqual!T == SysTime)) { result = value.toISOExtString; } else { result = value.to!string; } if(result.indexOf("const(") == 0) { result = result[6..$]; auto pos = result.indexOf(")"); result = result[0..pos] ~ result[pos + 1..$]; } if(result.indexOf("immutable(") == 0) { result = result[10..$]; auto pos = result.indexOf(")"); result = result[0..pos] ~ result[pos + 1..$]; } return result; } /// string serialize(T)(T value) if(!is(T == enum) && (isSomeString!T || (!isArray!T && !isAssociativeArray!T && !isAggregateType!T))) { static if(isSomeString!T) { return `"` ~ value.to!string ~ `"`; } else static if(isSomeChar!T) { return `'` ~ value.to!string ~ `'`; } else { return value.to!string; } } string serialize(T)(T value) if(is(T == enum)) { static foreach(member; EnumMembers!T) { if(member == value) { return this.serialize(cast(OriginalType!T) member); } } throw new Exception("The value can not be serialized."); } string niceValue(T)(T value) { static if(is(Unqual!T == SysTime)) { return value.toISOExtString; } else static if(is(Unqual!T == Duration)) { return value.to!string; } else { return serialize(value); } } } /// It should be able to override the default struct serializer unittest { struct A {} string serializer(A) { return "custom value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); registry.serialize(A()).should.equal("custom value"); registry.serialize([A()]).should.equal("[custom value]"); registry.serialize(["key": A()]).should.equal(`["key":custom value]`); } /// It should be able to override the default const struct serializer unittest { struct A {} string serializer(const A) { return "custom value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); const A value; registry.serialize(value).should.equal("custom value"); registry.serialize([value]).should.equal("[custom value]"); registry.serialize(["key": value]).should.equal(`["key":custom value]`); } /// It should be able to override the default immutable struct serializer unittest { struct A {} string serializer(immutable A) { return "value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); immutable A ivalue; const A cvalue; A value; registry.serialize(value).should.equal("A()"); registry.serialize(cvalue).should.equal("A()"); registry.serialize(ivalue).should.equal("value"); registry.serialize(ivalue).should.equal("value"); registry.serialize([ivalue]).should.equal("[value]"); registry.serialize(["key": ivalue]).should.equal(`["key":value]`); } /// It should be able to override the default class serializer unittest { class A {} string serializer(A) { return "custom value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); registry.serialize(new A()).should.equal("custom value"); registry.serialize([new A()]).should.equal("[custom value]"); registry.serialize(["key": new A()]).should.equal(`["key":custom value]`); } /// It should be able to override the default const class serializer unittest { class A {} string serializer(const A) { return "custom value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); const A value = new A; registry.serialize(value).should.equal("custom value"); registry.serialize([value]).should.equal("[custom value]"); registry.serialize(["key": value]).should.equal(`["key":custom value]`); } /// It should be able to override the default immutable class serializer unittest { class A {} string serializer(immutable A) { return "value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); immutable A ivalue; const A cvalue; A value; registry.serialize(value).should.equal("null"); registry.serialize(cvalue).should.equal("null"); registry.serialize(ivalue).should.equal("value"); registry.serialize(ivalue).should.equal("value"); registry.serialize([ivalue]).should.equal("[value]"); registry.serialize(["key": ivalue]).should.equal(`["key":value]`); } /// It should serialize a char unittest { char ch = 'a'; const char cch = 'a'; immutable char ich = 'a'; SerializerRegistry.instance.serialize(ch).should.equal("'a'"); SerializerRegistry.instance.serialize(cch).should.equal("'a'"); SerializerRegistry.instance.serialize(ich).should.equal("'a'"); } /// It should serialize a SysTime unittest { SysTime val = SysTime.fromISOExtString("2010-07-04T07:06:12"); const SysTime cval = SysTime.fromISOExtString("2010-07-04T07:06:12"); immutable SysTime ival = SysTime.fromISOExtString("2010-07-04T07:06:12"); SerializerRegistry.instance.serialize(val).should.equal("2010-07-04T07:06:12"); SerializerRegistry.instance.serialize(cval).should.equal("2010-07-04T07:06:12"); SerializerRegistry.instance.serialize(ival).should.equal("2010-07-04T07:06:12"); } /// It should serialize a string unittest { string str = "aaa"; const string cstr = "aaa"; immutable string istr = "aaa"; SerializerRegistry.instance.serialize(str).should.equal(`"aaa"`); SerializerRegistry.instance.serialize(cstr).should.equal(`"aaa"`); SerializerRegistry.instance.serialize(istr).should.equal(`"aaa"`); } /// It should serialize an int unittest { int value = 23; const int cvalue = 23; immutable int ivalue = 23; SerializerRegistry.instance.serialize(value).should.equal(`23`); SerializerRegistry.instance.serialize(cvalue).should.equal(`23`); SerializerRegistry.instance.serialize(ivalue).should.equal(`23`); } /// It should serialize an int list unittest { int[] value = [2,3]; const int[] cvalue = [2,3]; immutable int[] ivalue = [2,3]; SerializerRegistry.instance.serialize(value).should.equal(`[2, 3]`); SerializerRegistry.instance.serialize(cvalue).should.equal(`[2, 3]`); SerializerRegistry.instance.serialize(ivalue).should.equal(`[2, 3]`); } /// It should serialize a void list unittest { void[] value = []; const void[] cvalue = []; immutable void[] ivalue = []; SerializerRegistry.instance.serialize(value).should.equal(`[]`); SerializerRegistry.instance.serialize(cvalue).should.equal(`[]`); SerializerRegistry.instance.serialize(ivalue).should.equal(`[]`); } /// It should serialize a nested int list unittest { int[][] value = [[0,1],[2,3]]; const int[][] cvalue = [[0,1],[2,3]]; immutable int[][] ivalue = [[0,1],[2,3]]; SerializerRegistry.instance.serialize(value).should.equal(`[[0, 1], [2, 3]]`); SerializerRegistry.instance.serialize(cvalue).should.equal(`[[0, 1], [2, 3]]`); SerializerRegistry.instance.serialize(ivalue).should.equal(`[[0, 1], [2, 3]]`); } /// It should serialize an assoc array unittest { int[string] value = ["a": 2,"b": 3, "c": 4]; const int[string] cvalue = ["a": 2,"b": 3, "c": 4]; immutable int[string] ivalue = ["a": 2,"b": 3, "c": 4]; SerializerRegistry.instance.serialize(value).should.equal(`["a":2, "b":3, "c":4]`); SerializerRegistry.instance.serialize(cvalue).should.equal(`["a":2, "b":3, "c":4]`); SerializerRegistry.instance.serialize(ivalue).should.equal(`["a":2, "b":3, "c":4]`); } /// It should serialize a string enum unittest { enum TestType : string { a = "a", b = "b" } TestType value = TestType.a; const TestType cvalue = TestType.a; immutable TestType ivalue = TestType.a; SerializerRegistry.instance.serialize(value).should.equal(`"a"`); SerializerRegistry.instance.serialize(cvalue).should.equal(`"a"`); SerializerRegistry.instance.serialize(ivalue).should.equal(`"a"`); } version(unittest) { struct TestStruct { int a; string b; }; } /// It should serialize a struct unittest { TestStruct value = TestStruct(1, "2"); const TestStruct cvalue = TestStruct(1, "2"); immutable TestStruct ivalue = TestStruct(1, "2"); SerializerRegistry.instance.serialize(value).should.equal(`TestStruct(1, "2")`); SerializerRegistry.instance.serialize(cvalue).should.equal(`TestStruct(1, "2")`); SerializerRegistry.instance.serialize(ivalue).should.equal(`TestStruct(1, "2")`); } string unqualString(T: U[], U)() if(isArray!T && !isSomeString!T) { return unqualString!U ~ "[]"; } string unqualString(T: V[K], V, K)() if(isAssociativeArray!T) { return unqualString!V ~ "[" ~ unqualString!K ~ "]"; } string unqualString(T)() if(isSomeString!T || (!isArray!T && !isAssociativeArray!T)) { static if(is(T == class) || is(T == struct) || is(T == interface)) { return fullyQualifiedName!(Unqual!(T)); } else { return Unqual!T.stringof; } } string joinClassTypes(T)() { string result; static if(is(T == class)) { static foreach(Type; BaseClassesTuple!T) { result ~= Type.stringof; } } static if(is(T == interface) || is(T == class)) { static foreach(Type; InterfacesTuple!T) { if(result.length > 0) result ~= ":"; result ~= Type.stringof; } } static if(!is(T == interface) && !is(T == class)) { result = Unqual!T.stringof; } return result; } /// string[] parseList(string value) @safe nothrow { if(value.length == 0) { return []; } if(value.length == 1) { return [ value ]; } if(value[0] != '[' || value[value.length - 1] != ']') { return [ value ]; } string[] result; string currentValue; bool isInsideString; bool isInsideChar; bool isInsideArray; long arrayIndex = 0; foreach(index; 1..value.length - 1) { auto ch = value[index]; auto canSplit = !isInsideString && !isInsideChar && !isInsideArray; if(canSplit && ch == ',' && currentValue.length > 0) { result ~= currentValue.strip.dup; currentValue = ""; continue; } if(!isInsideChar && !isInsideString) { if(ch == '[') { arrayIndex++; isInsideArray = true; } if(ch == ']') { arrayIndex--; if(arrayIndex == 0) { isInsideArray = false; } } } if(!isInsideArray) { if(!isInsideChar && ch == '"') { isInsideString = !isInsideString; } if(!isInsideString && ch == '\'') { isInsideChar = !isInsideChar; } } currentValue ~= ch; } if(currentValue.length > 0) { result ~= currentValue.strip; } return result; } /// it should parse an empty string unittest { auto pieces = "".parseList; pieces.should.equal([]); } /// it should not parse a string that does not contain [] unittest { auto pieces = "test".parseList; pieces.should.equal([ "test" ]); } /// it should not parse a char that does not contain [] unittest { auto pieces = "t".parseList; pieces.should.equal([ "t" ]); } /// it should parse an empty array unittest { auto pieces = "[]".parseList; pieces.should.equal([]); } /// it should parse a list of one number unittest { auto pieces = "[1]".parseList; pieces.should.equal(["1"]); } /// it should parse a list of two numbers unittest { auto pieces = "[1,2]".parseList; pieces.should.equal(["1","2"]); } /// it should remove the whitespaces from the parsed values unittest { auto pieces = "[ 1, 2 ]".parseList; pieces.should.equal(["1","2"]); } /// it should parse two string values that contain a `,` unittest { auto pieces = `[ "a,b", "c,d" ]`.parseList; pieces.should.equal([`"a,b"`,`"c,d"`]); } /// it should parse two string values that contain a `'` unittest { auto pieces = `[ "a'b", "c'd" ]`.parseList; pieces.should.equal([`"a'b"`,`"c'd"`]); } /// it should parse two char values that contain a `,` unittest { auto pieces = `[ ',' , ',' ]`.parseList; pieces.should.equal([`','`,`','`]); } /// it should parse two char values that contain `[` and `]` unittest { auto pieces = `[ '[' , ']' ]`.parseList; pieces.should.equal([`'['`,`']'`]); } /// it should parse two string values that contain `[` and `]` unittest { auto pieces = `[ "[" , "]" ]`.parseList; pieces.should.equal([`"["`,`"]"`]); } /// it should parse two char values that contain a `"` unittest { auto pieces = `[ '"' , '"' ]`.parseList; pieces.should.equal([`'"'`,`'"'`]); } /// it should parse two empty lists unittest { auto pieces = `[ [] , [] ]`.parseList; pieces.should.equal([`[]`,`[]`]); } /// it should parse two nested lists unittest { auto pieces = `[ [[],[]] , [[[]],[]] ]`.parseList; pieces.should.equal([`[[],[]]`,`[[[]],[]]`]); } /// it should parse two lists with items unittest { auto pieces = `[ [1,2] , [3,4] ]`.parseList; pieces.should.equal([`[1,2]`,`[3,4]`]); } /// it should parse two lists with string and char items unittest { auto pieces = `[ ["1", "2"] , ['3', '4'] ]`.parseList; pieces.should.equal([`["1", "2"]`,`['3', '4']`]); } /// it should parse two lists with string and char items unittest { auto pieces = `[ ["1", "2"] , ['3', '4'] ]`.parseList; pieces.should.equal([`["1", "2"]`,`['3', '4']`]); } /// string cleanString(string value) @safe nothrow { if(value.length <= 1) { return value; } char first = value[0]; char last = value[value.length - 1]; if(first == last && (first == '"' || first == '\'')) { return value[1..$-1]; } return value; } /// it should return an empty string when the input is an empty string unittest { "".cleanString.should.equal(""); } /// it should return the input value when it has one char unittest { "'".cleanString.should.equal("'"); } /// it should remove the " from start and end of the string unittest { `""`.cleanString.should.equal(``); } /// it should remove the ' from start and end of the string unittest { `''`.cleanString.should.equal(``); } /// string[] cleanString(string[] pieces) @safe nothrow { return pieces.map!(a => a.cleanString).array; } /// It should return an empty array when the input list is empty unittest { string[] empty; empty.cleanString.should.equal(empty); } /// It should remove the `"` from the begin and end of the string unittest { [`"1"`, `"2"`].cleanString.should.equal([`1`, `2`]); }
module fluentasserts.core.serializers; import std.array; import std.string; import std.algorithm; import std.traits; import std.conv; import std.datetime; import std.functional; version(unittest) import fluent.asserts; /// Singleton used to serialize to string the tested values class SerializerRegistry { static SerializerRegistry instance; private { string delegate(void*)[string] serializers; string delegate(const void*)[string] constSerializers; string delegate(immutable void*)[string] immutableSerializers; } /// void register(T)(string delegate(T) serializer) if(isAggregateType!T) { enum key = T.stringof; static if(is(Unqual!T == T)) { string wrap(void* val) { auto value = (cast(T*) val); return serializer(*value); } serializers[key] = &wrap; } else static if(is(ConstOf!T == T)) { string wrap(const void* val) { auto value = (cast(T*) val); return serializer(*value); } constSerializers[key] = &wrap; } else static if(is(ImmutableOf!T == T)) { string wrap(immutable void* val) { auto value = (cast(T*) val); return serializer(*value); } immutableSerializers[key] = &wrap; } } void register(T)(string function(T) serializer) { auto serializerDelegate = serializer.toDelegate; this.register(serializerDelegate); } /// string serialize(T)(T[] value) if(!isSomeString!(T[])) { static if(is(Unqual!T == void)) { return "[]"; } else { return "[" ~ value.map!(a => serialize(a)).joiner(", ").array.to!string ~ "]"; } } /// string serialize(T: V[K], V, K)(T value) { auto keys = value.byKey.array.sort; return "[" ~ keys.map!(a => serialize(a) ~ ":" ~ serialize(value[a])).joiner(", ").array.to!string ~ "]"; } /// string serialize(T)(T value) if(isAggregateType!T) { auto key = T.stringof; auto tmp = &value; static if(is(Unqual!T == T)) { if(key in serializers) { return serializers[key](tmp); } } static if(is(ConstOf!T == T)) { if(key in constSerializers) { return constSerializers[key](tmp); } } static if(is(ImmutableOf!T == T)) { if(key in immutableSerializers) { return immutableSerializers[key](tmp); } } string result; static if(is(T == class)) { if(value is null) { result = "null"; } else { auto v = (cast() value); result = T.stringof ~ "(" ~ v.toHash.to!string ~ ")"; } } else static if(is(Unqual!T == Duration)) { result = value.total!"nsecs".to!string; } else static if(is(Unqual!T == SysTime)) { result = value.toISOExtString; } else { result = value.to!string; } if(result.indexOf("const(") == 0) { result = result[6..$]; auto pos = result.indexOf(")"); result = result[0..pos] ~ result[pos + 1..$]; } if(result.indexOf("immutable(") == 0) { result = result[10..$]; auto pos = result.indexOf(")"); result = result[0..pos] ~ result[pos + 1..$]; } return result; } /// string serialize(T)(T value) if(!is(T == enum) && (isSomeString!T || (!isArray!T && !isAssociativeArray!T && !isAggregateType!T))) { static if(isSomeString!T) { return `"` ~ value.to!string ~ `"`; } else static if(isSomeChar!T) { return `'` ~ value.to!string ~ `'`; } else { return value.to!string; } } string serialize(T)(T value) if(is(T == enum)) { static foreach(member; EnumMembers!T) { if(member == value) { return this.serialize(cast(OriginalType!T) member); } } throw new Exception("The value can not be serialized."); } string niceValue(T)(T value) { static if(is(Unqual!T == SysTime)) { return value.toISOExtString; } else static if(is(Unqual!T == Duration)) { return value.to!string; } else { return serialize(value); } } } /// It should be able to override the default struct serializer unittest { struct A {} string serializer(A) { return "custom value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); registry.serialize(A()).should.equal("custom value"); registry.serialize([A()]).should.equal("[custom value]"); registry.serialize(["key": A()]).should.equal(`["key":custom value]`); } /// It should be able to override the default const struct serializer unittest { struct A {} string serializer(const A) { return "custom value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); const A value; registry.serialize(value).should.equal("custom value"); registry.serialize([value]).should.equal("[custom value]"); registry.serialize(["key": value]).should.equal(`["key":custom value]`); } /// It should be able to override the default immutable struct serializer unittest { struct A {} string serializer(immutable A) { return "value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); immutable A ivalue; const A cvalue; A value; registry.serialize(value).should.equal("A()"); registry.serialize(cvalue).should.equal("A()"); registry.serialize(ivalue).should.equal("value"); registry.serialize(ivalue).should.equal("value"); registry.serialize([ivalue]).should.equal("[value]"); registry.serialize(["key": ivalue]).should.equal(`["key":value]`); } /// It should be able to override the default class serializer unittest { class A {} string serializer(A) { return "custom value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); registry.serialize(new A()).should.equal("custom value"); registry.serialize([new A()]).should.equal("[custom value]"); registry.serialize(["key": new A()]).should.equal(`["key":custom value]`); } /// It should be able to override the default const class serializer unittest { class A {} string serializer(const A) { return "custom value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); const A value = new A; registry.serialize(value).should.equal("custom value"); registry.serialize([value]).should.equal("[custom value]"); registry.serialize(["key": value]).should.equal(`["key":custom value]`); } /// It should be able to override the default immutable class serializer unittest { class A {} string serializer(immutable A) { return "value"; } auto registry = new SerializerRegistry(); registry.register(&serializer); immutable A ivalue; const A cvalue; A value; registry.serialize(value).should.equal("null"); registry.serialize(cvalue).should.equal("null"); registry.serialize(ivalue).should.equal("value"); registry.serialize(ivalue).should.equal("value"); registry.serialize([ivalue]).should.equal("[value]"); registry.serialize(["key": ivalue]).should.equal(`["key":value]`); } /// It should serialize a char unittest { char ch = 'a'; const char cch = 'a'; immutable char ich = 'a'; SerializerRegistry.instance.serialize(ch).should.equal("'a'"); SerializerRegistry.instance.serialize(cch).should.equal("'a'"); SerializerRegistry.instance.serialize(ich).should.equal("'a'"); } /// It should serialize a SysTime unittest { SysTime val = SysTime.fromISOExtString("2010-07-04T07:06:12"); const SysTime cval = SysTime.fromISOExtString("2010-07-04T07:06:12"); immutable SysTime ival = SysTime.fromISOExtString("2010-07-04T07:06:12"); SerializerRegistry.instance.serialize(val).should.equal("2010-07-04T07:06:12"); SerializerRegistry.instance.serialize(cval).should.equal("2010-07-04T07:06:12"); SerializerRegistry.instance.serialize(ival).should.equal("2010-07-04T07:06:12"); } /// It should serialize a string unittest { string str = "aaa"; const string cstr = "aaa"; immutable string istr = "aaa"; SerializerRegistry.instance.serialize(str).should.equal(`"aaa"`); SerializerRegistry.instance.serialize(cstr).should.equal(`"aaa"`); SerializerRegistry.instance.serialize(istr).should.equal(`"aaa"`); } /// It should serialize an int unittest { int value = 23; const int cvalue = 23; immutable int ivalue = 23; SerializerRegistry.instance.serialize(value).should.equal(`23`); SerializerRegistry.instance.serialize(cvalue).should.equal(`23`); SerializerRegistry.instance.serialize(ivalue).should.equal(`23`); } /// It should serialize an int list unittest { int[] value = [2,3]; const int[] cvalue = [2,3]; immutable int[] ivalue = [2,3]; SerializerRegistry.instance.serialize(value).should.equal(`[2, 3]`); SerializerRegistry.instance.serialize(cvalue).should.equal(`[2, 3]`); SerializerRegistry.instance.serialize(ivalue).should.equal(`[2, 3]`); } /// It should serialize a void list unittest { void[] value = []; const void[] cvalue = []; immutable void[] ivalue = []; SerializerRegistry.instance.serialize(value).should.equal(`[]`); SerializerRegistry.instance.serialize(cvalue).should.equal(`[]`); SerializerRegistry.instance.serialize(ivalue).should.equal(`[]`); } /// It should serialize a nested int list unittest { int[][] value = [[0,1],[2,3]]; const int[][] cvalue = [[0,1],[2,3]]; immutable int[][] ivalue = [[0,1],[2,3]]; SerializerRegistry.instance.serialize(value).should.equal(`[[0, 1], [2, 3]]`); SerializerRegistry.instance.serialize(cvalue).should.equal(`[[0, 1], [2, 3]]`); SerializerRegistry.instance.serialize(ivalue).should.equal(`[[0, 1], [2, 3]]`); } /// It should serialize an assoc array unittest { int[string] value = ["a": 2,"b": 3, "c": 4]; const int[string] cvalue = ["a": 2,"b": 3, "c": 4]; immutable int[string] ivalue = ["a": 2,"b": 3, "c": 4]; SerializerRegistry.instance.serialize(value).should.equal(`["a":2, "b":3, "c":4]`); SerializerRegistry.instance.serialize(cvalue).should.equal(`["a":2, "b":3, "c":4]`); SerializerRegistry.instance.serialize(ivalue).should.equal(`["a":2, "b":3, "c":4]`); } /// It should serialize a string enum unittest { enum TestType : string { a = "a", b = "b" } TestType value = TestType.a; const TestType cvalue = TestType.a; immutable TestType ivalue = TestType.a; SerializerRegistry.instance.serialize(value).should.equal(`"a"`); SerializerRegistry.instance.serialize(cvalue).should.equal(`"a"`); SerializerRegistry.instance.serialize(ivalue).should.equal(`"a"`); } version(unittest) { struct TestStruct { int a; string b; }; } /// It should serialize a struct unittest { TestStruct value = TestStruct(1, "2"); const TestStruct cvalue = TestStruct(1, "2"); immutable TestStruct ivalue = TestStruct(1, "2"); SerializerRegistry.instance.serialize(value).should.equal(`TestStruct(1, "2")`); SerializerRegistry.instance.serialize(cvalue).should.equal(`TestStruct(1, "2")`); SerializerRegistry.instance.serialize(ivalue).should.equal(`TestStruct(1, "2")`); } string unqualString(T: U[], U)() if(isArray!T && !isSomeString!T) { return unqualString!U ~ "[]"; } string unqualString(T: V[K], V, K)() if(isAssociativeArray!T) { return unqualString!V ~ "[" ~ unqualString!K ~ "]"; } string unqualString(T)() if(isSomeString!T || (!isArray!T && !isAssociativeArray!T)) { static if(is(T == class) || is(T == struct) || is(T == interface)) { return fullyQualifiedName!(Unqual!(T)); } else { return Unqual!T.stringof; } } string joinClassTypes(T)() { string result; static if(is(T == class)) { static foreach(Type; BaseClassesTuple!T) { result ~= Type.stringof; } } static if(is(T == interface) || is(T == class)) { static foreach(Type; InterfacesTuple!T) { if(result.length > 0) result ~= ":"; result ~= Type.stringof; } } static if(!is(T == interface) && !is(T == class)) { result = Unqual!T.stringof; } return result; } /// string[] parseList(string value) @safe nothrow { if(value.length == 0) { return []; } if(value.length == 1) { return [ value ]; } if(value[0] != '[' || value[value.length - 1] != ']') { return [ value ]; } string[] result; string currentValue; bool isInsideString; bool isInsideChar; bool isInsideArray; long arrayIndex = 0; foreach(index; 1..value.length - 1) { auto ch = value[index]; auto canSplit = !isInsideString && !isInsideChar && !isInsideArray; if(canSplit && ch == ',' && currentValue.length > 0) { result ~= currentValue.strip.dup; currentValue = ""; continue; } if(!isInsideChar && !isInsideString) { if(ch == '[') { arrayIndex++; isInsideArray = true; } if(ch == ']') { arrayIndex--; if(arrayIndex == 0) { isInsideArray = false; } } } if(!isInsideArray) { if(!isInsideChar && ch == '"') { isInsideString = !isInsideString; } if(!isInsideString && ch == '\'') { isInsideChar = !isInsideChar; } } currentValue ~= ch; } if(currentValue.length > 0) { result ~= currentValue.strip; } return result; } /// it should parse an empty string unittest { auto pieces = "".parseList; pieces.should.equal([]); } /// it should not parse a string that does not contain [] unittest { auto pieces = "test".parseList; pieces.should.equal([ "test" ]); } /// it should not parse a char that does not contain [] unittest { auto pieces = "t".parseList; pieces.should.equal([ "t" ]); } /// it should parse an empty array unittest { auto pieces = "[]".parseList; pieces.should.equal([]); } /// it should parse a list of one number unittest { auto pieces = "[1]".parseList; pieces.should.equal(["1"]); } /// it should parse a list of two numbers unittest { auto pieces = "[1,2]".parseList; pieces.should.equal(["1","2"]); } /// it should remove the whitespaces from the parsed values unittest { auto pieces = "[ 1, 2 ]".parseList; pieces.should.equal(["1","2"]); } /// it should parse two string values that contain a `,` unittest { auto pieces = `[ "a,b", "c,d" ]`.parseList; pieces.should.equal([`"a,b"`,`"c,d"`]); } /// it should parse two string values that contain a `'` unittest { auto pieces = `[ "a'b", "c'd" ]`.parseList; pieces.should.equal([`"a'b"`,`"c'd"`]); } /// it should parse two char values that contain a `,` unittest { auto pieces = `[ ',' , ',' ]`.parseList; pieces.should.equal([`','`,`','`]); } /// it should parse two char values that contain `[` and `]` unittest { auto pieces = `[ '[' , ']' ]`.parseList; pieces.should.equal([`'['`,`']'`]); } /// it should parse two string values that contain `[` and `]` unittest { auto pieces = `[ "[" , "]" ]`.parseList; pieces.should.equal([`"["`,`"]"`]); } /// it should parse two char values that contain a `"` unittest { auto pieces = `[ '"' , '"' ]`.parseList; pieces.should.equal([`'"'`,`'"'`]); } /// it should parse two empty lists unittest { auto pieces = `[ [] , [] ]`.parseList; pieces.should.equal([`[]`,`[]`]); } /// it should parse two nested lists unittest { auto pieces = `[ [[],[]] , [[[]],[]] ]`.parseList; pieces.should.equal([`[[],[]]`,`[[[]],[]]`]); } /// it should parse two lists with items unittest { auto pieces = `[ [1,2] , [3,4] ]`.parseList; pieces.should.equal([`[1,2]`,`[3,4]`]); } /// it should parse two lists with string and char items unittest { auto pieces = `[ ["1", "2"] , ['3', '4'] ]`.parseList; pieces.should.equal([`["1", "2"]`,`['3', '4']`]); } /// it should parse two lists with string and char items unittest { auto pieces = `[ ["1", "2"] , ['3', '4'] ]`.parseList; pieces.should.equal([`["1", "2"]`,`['3', '4']`]); } /// string cleanString(string value) @safe nothrow { if(value.length <= 1) { return value; } char first = value[0]; char last = value[value.length - 1]; if(first == last && (first == '"' || first == '\'')) { return value[1..$-1]; } return value; } /// it should return an empty string when the input is an empty string unittest { "".cleanString.should.equal(""); } /// it should return the input value when it has one char unittest { "'".cleanString.should.equal("'"); } /// it should remove the " from start and end of the string unittest { `""`.cleanString.should.equal(``); } /// it should remove the ' from start and end of the string unittest { `''`.cleanString.should.equal(``); } /// string[] cleanString(string[] pieces) @safe nothrow { return pieces.map!(a => a.cleanString).array; } /// It should return an empty array when the input list is empty unittest { string[] empty; empty.cleanString.should.equal(empty); } /// It should remove the `"` from the begin and end of the string unittest { [`"1"`, `"2"`].cleanString.should.equal([`1`, `2`]); }