1 module godot.stringname;
2 
3 import std.traits;
4 import std.exception : assumeWontThrow;
5 import godot.builtins;
6 import godot.poolarrays;
7 import godot.abi;
8 import godot.abi.gdextension;
9 import godot.string;
10 
11 import godot.variant;
12 
13 /// Initializes an empty StringName
14 alias stringName = StringName.makeEmpty;
15 
16 struct StringName {
17     //@nogc nothrow:
18 
19     package(godot) union _StringName {
20         godot_string _godot_string_name;
21         StringName_Bind _bind;
22     }
23 
24     package(godot) _StringName _stringName;
25     alias _stringName this;
26 
27     this(String s) {
28         this = _bind.new2(s);
29     }
30 
31     this(this) {
32 
33     }
34 
35     this(string s) {
36         auto str = String(s);
37         this(str);
38     }
39     
40     /++
41 	Numeric constructor. S can be a built-in numeric type.
42 	+/
43     this(S)(in S num) if (isNumeric!S) {
44         import std.conv : text;
45         this(num.text);
46     }
47 
48     deprecated("Default struct ctor is not allowed, please use `stringName()` instead")
49     @disable this();
50 
51     //this(ref const StringName s)
52     //{
53     //	this = _bind.new1(s);
54     //}
55 
56     void _defaultCtor() {
57         this = StringName_Bind.new0();
58     }
59     
60     static StringName makeEmpty() {
61         StringName sn = void;
62         sn._defaultCtor();
63         return sn;
64     }
65 
66     /// Returns the length of the char32_t array, minus the zero terminator.
67     size_t length() const {
68         auto len = _godot_api.string_to_utf8_chars(&_godot_string_name, null, 0);
69         return len;
70         //return _godot_api.string_length(&_godot_string);
71     }
72 
73     /// Returns: $(D true) if length is 0
74     bool empty() const {
75         return length == 0;
76     }
77 
78     /// Returns a pointer to the wchar_t data. Always zero-terminated.
79     immutable(char32_t)* ptr() const {
80         return cast(immutable(char32_t)*) _godot_api.string_operator_index_const(
81             &_godot_string_name, 0);
82     }
83 
84     /// Returns a slice of the char32_t data without the zero terminator.
85     dstring data() const {
86         // in godot-cpp there is actually no such things like data(), ptr() and length() for StringName
87         return cast(typeof(return)) ptr[0 .. length];
88     }
89 
90     package(godot) this(in godot_string strname) {
91         _godot_string_name = strname;
92     }
93 
94     /++
95 	char constructor. S can be a slice (like `string`) or a null-terminated pointer.
96 	+/
97     this(S)(in S str)
98             if (isImplicitlyConvertible!(S, const(char)[]) ||
99             isImplicitlyConvertible!(S, const(char)*)) {
100         static if (isImplicitlyConvertible!(S, const(char)[])) {
101             const(char)[] contents = str;
102             _godot_api.string_new_with_latin1_chars_and_len(&_godot_string_name, contents.ptr, cast(
103                     int) contents.length);
104         } else {
105             import core.stdc.string : strlen;
106 
107             const(char)* contents = str;
108             _godot_api.string_new_with_latin1_chars_and_len(&_godot_string_name, contents, cast(int) strlen(
109                     contents));
110         }
111     }
112 
113     ~this() {
114         //_bind._destructor();
115         _godot_string_name = _godot_string_name.init;
116     }
117 
118     void opAssign(in StringName other) {
119         //if (&_godot_string_name)
120         //    _bind._destructor();
121 
122         _godot_string_name = other._godot_string_name;
123     }
124 
125     void opAssign(in string other) {
126         //if (&_godot_string_name)
127         //    _bind._destructor();
128         godot_string gs;
129         _godot_api.string_new_with_utf8_chars_and_len(&gs, other.ptr, cast(int) other.length);
130         _godot_string_name = gs;
131     }
132 
133     bool opEquals(in StringName other) const {
134         if (_godot_string_name == other._godot_string_name)
135             return true;
136         // FIXME: no idea if there is actually such thing
137         //return _godot_api.string_name_operator_equal(&_godot_string_name, &other._godot_string_name);
138         return false;
139     }
140 
141     String opCast(T : String)() const {
142         return String(_godot_string_name);
143     }
144 
145     GDExtensionStringNamePtr opCast(T = GDExtensionStringNamePtr)() const {
146         return cast(GDExtensionStringNamePtr) &_godot_string_name;
147     }
148 
149     @trusted
150     hash_t toHash() const nothrow {
151         return cast(hash_t) assumeWontThrow(_bind.hash());
152         //static if(hash_t.sizeof == uint.sizeof) return _godot_api.string_hash(&_godot_string);
153         //else return _godot_api.string_hash64(&_godot_string);
154     }
155 
156 }
157 
158 /** 
159  * Constructs Godot String Name from str
160  * Params:
161  *   str = string to convert from
162  * Returns: Godot String Name
163  */
164 StringName toGodotStringName(string str) {
165     // FIXME: this is going to be slow as hell
166     godot_string gs;
167     _godot_api.string_new_with_utf8_chars_and_len(&gs, str.ptr, cast(int) str.length);
168     String* p = cast(String*)&gs;
169     return StringName(*p);
170 }
171 
172 /** 
173  * Constructs string from str
174  * Params:
175  *   str = Godot String Name
176  * Returns: D string
177  */
178 string toDStringName(StringName str) {
179     // FIXME: this is going to be slow as hell
180     import std.conv: to;
181     return str.data.to!string;
182 }
183 
184 struct GodotStringNameLiteral(string data) {
185     private __gshared godot_string gs;
186     StringName str() const {
187         static if (data.length)
188             if (gs == godot_string.init) {
189                 synchronized {
190                     if (gs == godot_string.init)
191                         _godot_api.string_new_with_utf8_chars_and_len(&gs, data.ptr, cast(int) data
192                                 .length);
193                 }
194             }
195         // a pointer so it won't destroy itself ahead of time
196         String* p = cast(String*)&gs;
197         //String ret = void;
198         //_godot_api.variant_new_copy(&ret._godot_string, &gs);
199         //return ret;
200         return StringName(*p);
201     }
202 
203     static if (data.length) {
204         shared static ~this() {
205             //if(gs != godot_string.init) _godot_api.variant_destroy(&gs);
206         }
207     }
208     alias str this;
209 }
210 
211 /++
212 Create a GodotStringNameLiteral.
213 
214 D $(D string) to Godot $(D StringName) conversion is expensive and cannot be done
215 at compile time. This literal does the conversion once the first time it's
216 needed, then caches the StringName, allowing it to implicitly convert to StringName at
217 no run time cost.
218 +/
219 enum gn(string str) = GodotStringNameLiteral!str.init;