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