@@ -3,10 +3,11 @@ use crate::sys;
3
3
use crate :: NewRef ;
4
4
5
5
use std:: cmp:: Ordering ;
6
+ use std:: convert:: TryFrom ;
6
7
use std:: ffi:: CStr ;
7
8
use std:: fmt;
8
9
use std:: mem:: forget;
9
- use std:: ops:: Range ;
10
+ use std:: ops:: { Add , AddAssign , Index , Range } ;
10
11
use std:: slice;
11
12
use std:: str;
12
13
@@ -242,7 +243,7 @@ impl GodotString {
242
243
GodotString ( sys)
243
244
}
244
245
245
- /// Clones `sys` into a `GodotString` without droping `sys`
246
+ /// Clones `sys` into a `GodotString` without dropping `sys`
246
247
#[ doc( hidden) ]
247
248
#[ inline]
248
249
pub fn clone_from_sys ( sys : sys:: godot_string ) -> Self {
@@ -293,6 +294,151 @@ impl std::hash::Hash for GodotString {
293
294
}
294
295
}
295
296
297
+ impl Add < GodotString > for GodotString {
298
+ type Output = GodotString ;
299
+ #[ inline]
300
+ fn add ( self , other : GodotString ) -> GodotString {
301
+ & self + & other
302
+ }
303
+ }
304
+
305
+ impl Add < & GodotString > for & GodotString {
306
+ type Output = GodotString ;
307
+ #[ inline]
308
+ fn add ( self , other : & GodotString ) -> GodotString {
309
+ GodotString :: from_sys ( unsafe { ( get_api ( ) . godot_string_operator_plus ) ( & self . 0 , & other. 0 ) } )
310
+ }
311
+ }
312
+
313
+ impl < S > Add < S > for & GodotString
314
+ where
315
+ S : AsRef < str > ,
316
+ {
317
+ type Output = GodotString ;
318
+ #[ inline]
319
+ fn add ( self , other : S ) -> GodotString {
320
+ self . add ( & GodotString :: from_str ( other) )
321
+ }
322
+ }
323
+
324
+ /// `AddAssign` implementations copy the strings' contents since `GodotString` is immutable.
325
+ impl AddAssign < & GodotString > for GodotString {
326
+ #[ inline]
327
+ fn add_assign ( & mut self , other : & Self ) {
328
+ * self = & * self + other;
329
+ }
330
+ }
331
+
332
+ /// `AddAssign` implementations copy the strings' contents since `GodotString` is immutable.
333
+ impl AddAssign < GodotString > for GodotString {
334
+ #[ inline]
335
+ fn add_assign ( & mut self , other : Self ) {
336
+ * self += & other;
337
+ }
338
+ }
339
+
340
+ /// `AddAssign` implementations copy the strings' contents since `GodotString` is immutable.
341
+ impl < S > AddAssign < S > for GodotString
342
+ where
343
+ S : AsRef < str > ,
344
+ {
345
+ #[ inline]
346
+ fn add_assign ( & mut self , other : S ) {
347
+ self . add_assign ( & GodotString :: from_str ( other) )
348
+ }
349
+ }
350
+
351
+ impl PartialOrd for GodotString {
352
+ #[ inline]
353
+ fn partial_cmp ( & self , other : & GodotString ) -> Option < Ordering > {
354
+ Some ( self . cmp ( other) )
355
+ }
356
+ }
357
+
358
+ impl Ord for GodotString {
359
+ #[ inline]
360
+ fn cmp ( & self , other : & GodotString ) -> Ordering {
361
+ if self == other {
362
+ Ordering :: Equal
363
+ } else if unsafe { ( get_api ( ) . godot_string_operator_less ) ( & self . 0 , & other. 0 ) } {
364
+ Ordering :: Less
365
+ } else {
366
+ Ordering :: Greater
367
+ }
368
+ }
369
+ }
370
+
371
+ /// Type representing a character in Godot's native encoding. Can be converted to and
372
+ /// from `char`. Depending on the platform, this might not always be able to represent
373
+ /// a full code point.
374
+ #[ derive( Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Hash , Debug , Default ) ]
375
+ #[ repr( transparent) ]
376
+ pub struct GodotChar ( libc:: wchar_t ) ;
377
+
378
+ /// Error indicating that a `GodotChar` cannot be converted to a `char`.
379
+ #[ derive( Debug ) ]
380
+ pub enum GodotCharError {
381
+ /// The character cannot be represented as a Unicode code point.
382
+ InvalidCodePoint ,
383
+ /// The character's encoding cannot be determined on this platform (`wchar_t` is
384
+ /// not 8, 16, or 32-bits wide).
385
+ UnknownEncoding ,
386
+ /// The character is part of an incomplete encoding sequence.
387
+ IncompleteSequence ,
388
+ }
389
+
390
+ impl TryFrom < GodotChar > for char {
391
+ type Error = GodotCharError ;
392
+
393
+ #[ inline]
394
+ fn try_from ( c : GodotChar ) -> Result < Self , GodotCharError > {
395
+ match std:: mem:: size_of :: < libc:: wchar_t > ( ) {
396
+ 1 => std:: char:: from_u32 ( c. 0 as u32 ) . ok_or ( GodotCharError :: IncompleteSequence ) ,
397
+ 4 => std:: char:: from_u32 ( c. 0 as u32 ) . ok_or ( GodotCharError :: InvalidCodePoint ) ,
398
+ 2 => {
399
+ let mut iter = std:: char:: decode_utf16 ( std:: iter:: once ( c. 0 as u16 ) ) ;
400
+ let c = iter
401
+ . next ( )
402
+ . ok_or ( GodotCharError :: InvalidCodePoint ) ?
403
+ . map_err ( |_| GodotCharError :: IncompleteSequence ) ?;
404
+
405
+ assert ! (
406
+ iter. next( ) . is_none( ) ,
407
+ "it should be impossible to decode more than one code point from one u16"
408
+ ) ;
409
+
410
+ Ok ( c)
411
+ }
412
+ _ => Err ( GodotCharError :: UnknownEncoding ) ,
413
+ }
414
+ }
415
+ }
416
+
417
+ /// Does a best-effort conversion from `GodotChar` to char. If that is not possible,
418
+ /// the implementation returns `false`.
419
+ impl PartialEq < char > for GodotChar {
420
+ #[ inline]
421
+ fn eq ( & self , other : & char ) -> bool {
422
+ char:: try_from ( * self ) . map_or ( false , |this| this == * other)
423
+ }
424
+ }
425
+
426
+ /// The index operator provides a low-level view of characters in Godot's native encoding
427
+ /// that doesn't always correspond to Unicode code points one-to-one. This operation goes
428
+ /// through FFI. For intensive string operations, consider converting to a Rust `String`
429
+ /// first to avoid this cost.
430
+ impl Index < usize > for GodotString {
431
+ type Output = GodotChar ;
432
+ #[ inline]
433
+ fn index ( & self , index : usize ) -> & Self :: Output {
434
+ unsafe {
435
+ let c: * const libc:: wchar_t =
436
+ ( get_api ( ) . godot_string_operator_index ) ( self . sys ( ) as * mut _ , index as i32 ) ;
437
+ & * ( c as * const GodotChar )
438
+ }
439
+ }
440
+ }
441
+
296
442
// TODO: Is it useful to expose this type?
297
443
// Could just make it an internal detail of how to convert to a rust string.
298
444
#[ doc( hidden) ]
@@ -476,6 +622,30 @@ godot_test!(test_string {
476
622
let foo2 = foo. new_ref( ) ;
477
623
assert!( foo == foo2) ;
478
624
625
+ let bar: GodotString = "bar" . into( ) ;
626
+ let qux: GodotString = "qux" . into( ) ;
627
+ assert_eq!( & bar + & qux, "barqux" . into( ) ) ;
628
+
629
+ let baz: GodotString = "baz" . into( ) ;
630
+ assert_eq!( & baz + "corge" , "bazcorge" . into( ) ) ;
631
+
632
+ let mut bar2 = bar. new_ref( ) ;
633
+ bar2 += & qux;
634
+ assert_eq!( bar2, "barqux" . into( ) ) ;
635
+
636
+ let cmp1: GodotString = "foo" . into( ) ;
637
+ let cmp2: GodotString = "foo" . into( ) ;
638
+ let cmp3: GodotString = "bar" . into( ) ;
639
+ assert_eq!( cmp1 < cmp2, false , "equal should not be less than" ) ;
640
+ assert_eq!( cmp1 > cmp2, false , "equal should not be greater than" ) ;
641
+ assert_eq!( cmp1 < cmp3, false , "foo should be less than bar" ) ;
642
+ assert_eq!( cmp3 > cmp1, false , "bar should be greater than foo" ) ;
643
+
644
+ let index_string: GodotString = "bar" . into( ) ;
645
+ assert_eq!( index_string[ 0 ] , 'b' ) ;
646
+ assert_eq!( index_string[ 1 ] , 'a' ) ;
647
+ assert_eq!( index_string[ 2 ] , 'r' ) ;
648
+
479
649
let variant = Variant :: from_godot_string( & foo) ;
480
650
assert!( variant. get_type( ) == VariantType :: GodotString ) ;
481
651
0 commit comments