1 /++ 2 $(H1 Annotated value) 3 4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 Authors: Ilia Ki 6 Macros: 7 +/ 8 module mir.annotated; 9 10 import mir.internal.meta: basicElementType; 11 import mir.serde: serdeRegister, serdeAnnotation, serdeIsDynamicAlgebraic; 12 13 static immutable excMsg = "At least one annotation is required to create an annotated value."; 14 version (D_Exceptions) 15 static immutable exc = new Exception(excMsg); 16 17 /++ 18 A convenience definition of an annotated value. 19 20 A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member. 21 +/ 22 @serdeRegister 23 @serdeAnnotation 24 struct Annotated(T) { 25 /// 26 @serdeAnnotation 27 string[] annotations; 28 29 static if (!(is(T == union) || is(T == struct))) 30 private enum _alloc_ = false; 31 else 32 static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic")) 33 private enum _alloc_ = true; 34 else 35 { 36 import mir.algebraic: isVariant; 37 static if (isVariant!T) 38 private enum _alloc_ = true; 39 else 40 private enum _alloc_ = false; 41 } 42 43 static if (_alloc_) 44 { 45 /// 46 private T* _value; 47 /// 48 ref inout(T) value() inout @property 49 in(_value) 50 { 51 return *_value; 52 } 53 54 /// 55 ref T value(T value) @property return scope 56 { 57 if (_value is null) 58 { 59 _value = new T; 60 import core.lifetime: move; 61 *_value = move(value); 62 } 63 return *_value; 64 } 65 66 /// 67 bool opEquals(scope const Annotated rhs) scope const 68 { 69 return annotations == rhs.annotations && value == rhs.value; 70 } 71 72 size_t toHash() scope @trusted const pure nothrow @nogc 73 { 74 static if (__traits(compiles, hashOf(value))) 75 return hashOf(value); 76 else 77 { 78 debug pragma(msg, "Mir warning: can't compute hash. Expexted `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof); 79 return cast(size_t)_value; 80 } 81 } 82 } 83 else 84 { 85 /// 86 T value; 87 } 88 89 90 /++ 91 Params: 92 annotations = non-empty array of annotations 93 args = arguments to construct value with 94 +/ 95 this(Args...)(string[] annotations, Args args) @safe pure { 96 if (annotations.length == 0) 97 { 98 version (D_Exceptions) 99 { import mir.exception : toMutable; throw exc.toMutable; } 100 else 101 assert(0, excMsg); 102 } 103 import core.lifetime: forward; 104 this.annotations = annotations; 105 static if (_alloc_) 106 this._value = new T(forward!args); 107 else 108 static if (__traits(compiles, value = args)) 109 this.value = args; 110 else 111 static if (is(T == class)) 112 this.value = new T(forward!args); 113 else 114 this.value = T(forward!args); 115 } 116 117 // private alias E = .basicElementType!T; 118 119 import std.traits: isAssociativeArray, isAggregateType; 120 /// 121 int opCmp()(scope const typeof(this) rhs) scope const pure nothrow @nogc @system 122 // if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp"))) 123 { 124 if (auto d = __cmp(annotations, rhs.annotations)) 125 return d; 126 127 static if (__traits(compiles, __cmp(value, rhs.value))) 128 return __cmp(value, rhs.value); 129 else 130 static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U)) 131 return value.opCmp(rhs.value); 132 else 133 return value < rhs.value ? -1 : value > rhs.value ? +1 : 0; 134 } 135 } 136 137 /// 138 version(mir_test) 139 unittest 140 { 141 auto annotations = ["annotation"]; 142 static struct S {double x;} 143 auto as = Annotated!S(annotations, 5); 144 assert(as.annotations == annotations); 145 assert(as.value.x == 5); 146 147 static struct C {double x;} 148 auto ac = Annotated!S(annotations, 5); 149 assert(ac.annotations == annotations); 150 assert(ac.value.x == 5); 151 } 152 153 /// 154 version(mir_test) 155 unittest 156 { 157 import mir.algebraic; 158 auto annotations = ["annotation"]; 159 static struct S {double x;} 160 auto as = Annotated!(Variant!S)(annotations, 5); 161 assert(as.annotations == annotations); 162 assert(as.value.x == 5); 163 164 static struct C {double x;} 165 auto ac = Annotated!(Variant!S)(annotations, 5); 166 assert(ac.annotations == annotations); 167 assert(ac.value.x == 5); 168 } 169 170 /++ 171 A convenience definition of an annotated value. 172 173 A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member. 174 +/ 175 @serdeRegister 176 @serdeAnnotation 177 struct AnnotatedOnce(T) { 178 /// 179 @serdeAnnotation 180 string annotation; 181 182 static if (!(is(T == union) || is(T == struct))) 183 private enum _alloc_ = false; 184 else 185 static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic")) 186 private enum _alloc_ = true; 187 else 188 { 189 import mir.algebraic: isVariant; 190 static if (isVariant!T) 191 private enum _alloc_ = true; 192 else 193 private enum _alloc_ = false; 194 } 195 196 static if (_alloc_) 197 { 198 /// 199 private T* _value; 200 /// 201 ref inout(T) value() inout @property 202 { 203 return *_value; 204 } 205 206 /// 207 ref T value(T value) @property 208 { 209 if (_value is null) 210 { 211 _value = new T; 212 import core.lifetime: move; 213 *_value = move(value); 214 } 215 return *_value; 216 } 217 218 /// 219 bool opEquals(scope const AnnotatedOnce rhs) scope const 220 { 221 return annotation == rhs.annotation && value == rhs.value; 222 } 223 } 224 else 225 { 226 /// 227 T value; 228 } 229 230 231 /++ 232 Params: 233 annotation = non-empty array of annotation 234 args = arguments to construct value with 235 +/ 236 this(Args...)(string annotation, Args args) @safe pure { 237 import core.lifetime: forward; 238 this.annotation = annotation; 239 static if (_alloc_) 240 this._value = new T(forward!args); 241 else 242 static if (__traits(compiles, value = args)) 243 this.value = args; 244 else 245 static if (is(T == class)) 246 this.value = new T(forward!args); 247 else 248 this.value = T(forward!args); 249 } 250 251 // private alias E = .basicElementType!T; 252 253 import std.traits: isAssociativeArray, isAggregateType; 254 // static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp"))) 255 /// 256 int opCmp()(scope const typeof(this) rhs) scope const @system pure nothrow @nogc 257 { 258 if (auto d = __cmp(annotation, rhs.annotation)) 259 return d; 260 261 static if (__traits(compiles, __cmp(value, rhs.value))) 262 return __cmp(value, rhs.value); 263 else 264 static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U)) 265 return value.opCmp(rhs.value); 266 else 267 return value < rhs.value ? -1 : value > rhs.value ? +1 : 0; 268 } 269 } 270 271 /// 272 version(mir_test) 273 unittest 274 { 275 auto annotation = "annotation"; 276 static struct S {double x;} 277 auto as = AnnotatedOnce!S(annotation, 5); 278 assert(as.annotation == annotation); 279 assert(as.value.x == 5); 280 281 static struct C {double x;} 282 auto ac = AnnotatedOnce!S(annotation, 5); 283 assert(ac.annotation == annotation); 284 assert(ac.value.x == 5); 285 } 286 287 /// 288 version(mir_test) 289 unittest 290 { 291 import mir.algebraic; 292 auto annotation = "annotation"; 293 static struct S {double x;} 294 auto as = AnnotatedOnce!(Variant!S)(annotation, 5); 295 assert(as.annotation == annotation); 296 assert(as.value.x == 5); 297 298 static struct C {double x;} 299 auto ac = AnnotatedOnce!(Variant!S)(annotation, 5); 300 assert(ac.annotation == annotation); 301 assert(ac.value.x == 5); 302 }