1 /++ 2 $(H1 Transformation utilities for JSON-like values) 3 4 See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf); 5 6 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 Authors: Ilia Ki 8 Macros: 9 +/ 10 module mir.algebraic_alias.transform; 11 12 import mir.algebraic: Algebraic, tryVisit, visit, optionalVisit; 13 import mir.functional: naryFun; 14 private alias AliasSeq(T...) = T; 15 16 /++ 17 Transforms algebraics leafs recursively in place, 18 ensuring that all leaf types are handled by the visiting functions. 19 20 Recursion is done for `This[]`, `StringMap!This`, `This[string]`, and `Annotated!This` types. 21 +/ 22 alias transformLeafs(visitors...) = transformLeafsImpl!(visit, naryFun!visitors); 23 24 /// 25 version(mir_test) 26 unittest 27 { 28 import mir.format: text; 29 import mir.algebraic_alias.json; 30 JsonAlgebraic value = ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, null.JsonAlgebraic].JsonAlgebraic]; 31 32 // converts all leavs to a text form 33 value.transformLeafs!text; 34 assert(value == ["key" : ["str".JsonAlgebraic, "2.32".JsonAlgebraic, "null".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic); 35 36 value = ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, true.JsonAlgebraic].JsonAlgebraic].JsonAlgebraic; 37 38 /// converts only bool values 39 value.transformLeafs!( 40 (bool b) => b.text, 41 v => v, // other values are copied as is 42 ); 43 44 assert(value == ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, "true".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic); 45 } 46 47 /++ 48 Behaves as $(LREF transformLeafs) but doesn't enforce at compile time that all types can be handled by the visiting functions. 49 50 Throws: Exception if `naryFun!visitors` can't be called with provided arguments 51 +/ 52 alias tryTransformLeafs(visitors...) = transformLeafsImpl!(tryVisit, naryFun!visitors); 53 54 /// 55 version(mir_test) 56 unittest 57 { 58 import mir.format: text; 59 import mir.algebraic_alias.json; 60 JsonAlgebraic value = ["key" : [true.JsonAlgebraic, 100.JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic]; 61 62 // converts long and double numbers to a text form, bool values is converted to `long` 63 value.tryTransformLeafs!((double v) => v.text, (long v) => v.text); 64 assert(value == ["key" : ["1".JsonAlgebraic, "100".JsonAlgebraic, "2.32".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic); 65 } 66 67 /++ 68 Behaves as $(LREF transformLeafs) but doesn't enforce at compile time that all types can be handled by the visiting functions. 69 70 The function ignores leafs that can't be handled by the visiting functions. 71 +/ 72 alias optionalTransformLeafs(visitors...) = transformLeafsImpl!(optionalVisit, naryFun!visitors); 73 74 /// 75 version(mir_test) 76 unittest 77 { 78 import mir.format: text; 79 import mir.algebraic_alias.json; 80 JsonAlgebraic value = ["key" : [null.JsonAlgebraic, true.JsonAlgebraic, 100.JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic]; 81 82 // converts long numbers to a text form, ignores other types 83 value.optionalTransformLeafs!( 84 (long v) => v.text, 85 (bool b) => b, // needs special overload for bool to get rid of implicit converion to long/double 86 ); 87 assert(value == ["key" : [null.JsonAlgebraic, true.JsonAlgebraic, "100".JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic].JsonAlgebraic); 88 } 89 90 /// 91 template transformLeafsImpl(alias handler, alias visitor) 92 { 93 /// 94 ref Algebraic!Types transformLeafsImpl(Types...)(ref return Algebraic!Types value) 95 { 96 import core.lifetime: move; 97 import mir.algebraic: visit; 98 import mir.annotated: Annotated; 99 import mir.string_map: StringMap; 100 alias T = Algebraic!Types; 101 static if (is(T.AllowedTypes[0] == typeof(null))) 102 { 103 enum nullCompiles = __traits(compiles, value = visitor(null)); 104 static if (nullCompiles || __traits(isSame, handler, visit)) 105 { 106 alias nullHandler = (typeof(null)) { 107 static if (nullCompiles) 108 value = visitor(null); 109 else 110 assert(0, "Null " ~ T.stringof); 111 }; 112 } 113 else 114 { 115 alias nullHandler = AliasSeq!(); 116 } 117 } 118 else 119 { 120 alias nullHandler = AliasSeq!(); 121 } 122 handler!( 123 (T[] v) { 124 foreach (ref e; v) 125 transformLeafsImpl(e); 126 }, 127 (StringMap!T v) { 128 foreach (ref e; v.values) 129 transformLeafsImpl(e); 130 }, 131 (T[string] v) { 132 foreach (key, ref e; v) 133 transformLeafsImpl(e); 134 }, 135 (Annotated!T v) { 136 transformLeafsImpl(v.value); 137 }, 138 nullHandler, 139 (ref v) { // auto for typeof(null) support 140 static if (__traits(compiles, value = visitor(move(v)))) 141 value = visitor(move(v)); 142 else 143 value = visitor(v); 144 } 145 )(value); 146 return value; 147 } 148 149 /// ditto 150 Algebraic!Types transformLeafsImpl(Types...)(Algebraic!Types value) 151 { 152 import core.lifetime: move; 153 transformLeafsImpl(value); 154 return move(value); 155 } 156 }