1 /** 2 Godot's ref-counted wchar_t String class. 3 4 Copyright: 5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 10 11 12 */ 13 module godot.core..string; 14 15 import core.stdc.stddef : wchar_t; 16 import godot.c; 17 import std.traits; 18 19 import godot.core.variant; 20 21 struct CharString 22 { 23 @nogc nothrow: 24 25 package(godot) godot_char_string _char_string; 26 27 immutable(char)* ptr() const 28 { 29 return cast(typeof(return))_godot_api.godot_char_string_get_data(&_char_string); 30 } 31 32 size_t length() const 33 { 34 return _godot_api.godot_char_string_length(&_char_string); 35 } 36 37 bool empty() const 38 { 39 return length == 0; 40 } 41 42 immutable(char)[] data() const 43 { 44 return ptr[0..length]; 45 } 46 47 ~this() 48 { 49 _godot_api.godot_char_string_destroy(&_char_string); 50 } 51 } 52 53 /** 54 This is the built-in string class (and the one used by GDScript). It supports Unicode and provides all necessary means for string handling. Strings are reference counted and use a copy-on-write approach, so passing them around is cheap in resources. 55 */ 56 struct String 57 { 58 @nogc nothrow: 59 60 package(godot) godot_string _godot_string; 61 62 /// postblit (Vector is CoW, so no data copying is done) 63 this(this) 64 { 65 godot_string other = _godot_string; 66 _godot_api.godot_string_new_copy(&_godot_string, &other); 67 } 68 69 package(godot) this(in godot_string str) 70 { 71 _godot_string = str; 72 } 73 74 /++ 75 wchar_t constructor. S can be a slice or a null-terminated pointer. 76 +/ 77 this(S)(in S str) if(isImplicitlyConvertible!(S, const(wchar_t)[]) || 78 isImplicitlyConvertible!(S, const(wchar_t)*)) 79 { 80 static if(isImplicitlyConvertible!(S, const(wchar_t)[])) 81 { 82 const(wchar_t)[] contents = str; 83 _godot_api.godot_string_new_with_wide_string(&_godot_string, contents.ptr, cast(int)contents.length); 84 } 85 else 86 { 87 import core.stdc.wchar_ : wcslen; 88 const(wchar_t)* contents = str; 89 _godot_api.godot_string_new_with_wide_string(&_godot_string, contents, cast(int)wcslen(contents)); 90 } 91 } 92 93 /++ 94 UTF-8 constructor. S can be a slice (like `string`) or a null-terminated pointer. 95 +/ 96 this(S)(in S str) if(isImplicitlyConvertible!(S, const(char)[]) || 97 isImplicitlyConvertible!(S, const(char)*)) 98 { 99 static if(isImplicitlyConvertible!(S, const(char)[])) 100 { 101 const(char)[] contents = str; 102 _godot_api.godot_string_parse_utf8_with_len(&_godot_string, contents.ptr, cast(int)contents.length); 103 } 104 else 105 { 106 const(char)* contents = str; 107 _godot_api.godot_string_parse_utf8(&_godot_string, contents); 108 } 109 } 110 111 ~this() 112 { 113 _godot_api.godot_string_destroy(&_godot_string); 114 } 115 116 117 void opAssign(in String other) 118 { 119 _godot_api.godot_string_destroy(&_godot_string); 120 _godot_api.godot_string_new_copy(&_godot_string, &other._godot_string); 121 } 122 123 124 /+String substr(int p_from,int p_chars) const 125 { 126 return String.empty; // todo 127 } 128 129 alias opSlice = substr;+/ 130 131 132 ref wchar_t opIndex(in size_t idx) 133 { 134 return *_godot_api.godot_string_operator_index(&_godot_string, cast(int)idx); 135 } 136 137 wchar_t opIndex(in size_t idx) const 138 { 139 return *_godot_api.godot_string_operator_index(cast(godot_string*) &_godot_string, cast(int)idx); 140 } 141 142 /// Returns the length of the wchar_t array, minus the zero terminator. 143 size_t length() const 144 { 145 return _godot_api.godot_string_length(&_godot_string); 146 } 147 148 /// Returns: $(D true) if length is 0 149 bool empty() const 150 { 151 return length == 0; 152 } 153 154 int opCmp(in String s) const 155 { 156 if(_godot_string == s._godot_string) return true; 157 auto equal = _godot_api.godot_string_operator_equal(&_godot_string, &s._godot_string); 158 if(equal) return 0; 159 auto less = _godot_api.godot_string_operator_less(&_godot_string, &s._godot_string); 160 return less?(-1):1; 161 } 162 bool opEquals(in String other) const 163 { 164 if(_godot_string == other._godot_string) return true; 165 return _godot_api.godot_string_operator_equal(&_godot_string, &other._godot_string); 166 } 167 168 String opBinary(string op)(in String other) const if(op == "~" || op == "+") 169 { 170 String ret = void; 171 ret._godot_string = _godot_api.godot_string_operator_plus(&_godot_string, &other._godot_string); 172 173 return ret; 174 } 175 176 void opOpAssign(string op)(in String other) if(op == "~" || op == "+") 177 { 178 _godot_string = _godot_api.godot_string_operator_plus(&_godot_string, &other._godot_string); 179 } 180 181 /// Returns a pointer to the wchar_t data. Always zero-terminated. 182 immutable(wchar_t)* ptr() const 183 { 184 return cast(typeof(return))_godot_api.godot_string_wide_str(&_godot_string); 185 } 186 187 /// Returns a slice of the wchar_t data without the zero terminator. 188 immutable(wchar_t)[] data() const 189 { 190 return ptr[0..length]; 191 } 192 alias toString = data; 193 194 CharString utf8() const 195 { 196 CharString ret = void; 197 ret._char_string = _godot_api.godot_string_utf8(&_godot_string); 198 return ret; 199 } 200 201 String format(V)(V values) const if(is(V : Variant) || Variant.compatibleToGodot!V) 202 { 203 const Variant v = values; 204 String new_string = void; 205 new_string._godot_string = _godot_api.godot_string_format(&_godot_string, cast(godot_variant *)&v); 206 207 return new_string; 208 } 209 210 String format(V)(V values, String placeholder) const if(is(V : Variant) || Variant.compatibleToGodot!V) 211 { 212 const Variant v = values; 213 String new_string = void; 214 CharString contents = placeholder.utf8; 215 new_string._godot_string = _godot_api.godot_string_format_with_custom_placeholder(&_godot_string, cast(godot_variant *)&v, contents.ptr); 216 217 return new_string; 218 } 219 220 @trusted 221 hash_t toHash() const 222 { 223 static if(hash_t.sizeof == uint.sizeof) return _godot_api.godot_string_hash(&_godot_string); 224 else return _godot_api.godot_string_hash64(&_godot_string); 225 } 226 } 227 228 struct GodotStringLiteral(string data) 229 { 230 private __gshared godot_string gs; 231 String str() const 232 { 233 static if(data.length) if(gs == godot_string.init) 234 { 235 synchronized 236 { 237 if(gs == godot_string.init) _godot_api.godot_string_parse_utf8_with_len(&gs, data.ptr, cast(int)data.length); 238 } 239 } 240 String ret = void; 241 _godot_api.godot_string_new_copy(&ret._godot_string, &gs); 242 return ret; 243 } 244 static if(data.length) 245 { 246 shared static ~this() 247 { 248 if(gs != godot_string.init) _godot_api.godot_string_destroy(&gs); 249 } 250 } 251 alias str this; 252 } 253 254 /++ 255 Create a GodotStringLiteral. 256 257 D $(D string) to Godot $(D String) conversion is expensive and cannot be done 258 at compile time. This literal does the conversion once the first time it's 259 needed, then caches the String, allowing it to implicitly convert to String at 260 no run time cost. 261 +/ 262 enum gs(string str) = GodotStringLiteral!str.init; 263 264 265 266 267 268