@@ -43,6 +43,14 @@ impl PyString {
43
43
}
44
44
}
45
45
46
+ impl From < & str > for PyString {
47
+ fn from ( s : & str ) -> PyString {
48
+ PyString {
49
+ value : s. to_string ( ) ,
50
+ }
51
+ }
52
+ }
53
+
46
54
pub type PyStringRef = PyRef < PyString > ;
47
55
48
56
impl fmt:: Display for PyString {
@@ -396,9 +404,33 @@ impl PyString {
396
404
}
397
405
}
398
406
407
+ /// Return a titlecased version of the string where words start with an
408
+ /// uppercase character and the remaining characters are lowercase.
399
409
#[ pymethod]
400
410
fn title ( & self , _vm : & VirtualMachine ) -> String {
401
- make_title ( & self . value )
411
+ let mut title = String :: new ( ) ;
412
+ let mut previous_is_cased = false ;
413
+ for c in self . value . chars ( ) {
414
+ if c. is_lowercase ( ) {
415
+ if !previous_is_cased {
416
+ title. extend ( c. to_uppercase ( ) ) ;
417
+ } else {
418
+ title. push ( c) ;
419
+ }
420
+ previous_is_cased = true ;
421
+ } else if c. is_uppercase ( ) {
422
+ if previous_is_cased {
423
+ title. extend ( c. to_lowercase ( ) ) ;
424
+ } else {
425
+ title. push ( c) ;
426
+ }
427
+ previous_is_cased = true ;
428
+ } else {
429
+ previous_is_cased = false ;
430
+ title. push ( c) ;
431
+ }
432
+ }
433
+ title
402
434
}
403
435
404
436
#[ pymethod]
@@ -609,13 +641,34 @@ impl PyString {
609
641
vm. ctx . new_tuple ( new_tup)
610
642
}
611
643
644
+ /// Return `true` if the sequence is ASCII titlecase and the sequence is not
645
+ /// empty, `false` otherwise.
612
646
#[ pymethod]
613
647
fn istitle ( & self , _vm : & VirtualMachine ) -> bool {
614
648
if self . value . is_empty ( ) {
615
- false
616
- } else {
617
- self . value . split ( ' ' ) . all ( |word| word == make_title ( word) )
649
+ return false ;
618
650
}
651
+
652
+ let mut cased = false ;
653
+ let mut previous_is_cased = false ;
654
+ for c in self . value . chars ( ) {
655
+ if c. is_uppercase ( ) {
656
+ if previous_is_cased {
657
+ return false ;
658
+ }
659
+ previous_is_cased = true ;
660
+ cased = true ;
661
+ } else if c. is_lowercase ( ) {
662
+ if !previous_is_cased {
663
+ return false ;
664
+ }
665
+ previous_is_cased = true ;
666
+ cased = true ;
667
+ } else {
668
+ previous_is_cased = false ;
669
+ }
670
+ }
671
+ cased
619
672
}
620
673
621
674
#[ pymethod]
@@ -981,22 +1034,55 @@ fn adjust_indices(
981
1034
}
982
1035
}
983
1036
984
- // helper function to title strings
985
- fn make_title ( s : & str ) -> String {
986
- let mut titled_str = String :: new ( ) ;
987
- let mut capitalize_char: bool = true ;
988
- for c in s. chars ( ) {
989
- if c. is_alphabetic ( ) {
990
- if !capitalize_char {
991
- titled_str. push ( c) ;
992
- } else if capitalize_char {
993
- titled_str. push ( c. to_ascii_uppercase ( ) ) ;
994
- capitalize_char = false ;
995
- }
996
- } else {
997
- titled_str. push ( c) ;
998
- capitalize_char = true ;
1037
+ #[ cfg( test) ]
1038
+ mod tests {
1039
+ use super :: * ;
1040
+
1041
+ #[ test]
1042
+ fn str_title ( ) {
1043
+ let vm = VirtualMachine :: new ( ) ;
1044
+
1045
+ let tests = vec ! [
1046
+ ( " Hello " , " hello " ) ,
1047
+ ( "Hello " , "hello " ) ,
1048
+ ( "Hello " , "Hello " ) ,
1049
+ ( "Format This As Title String" , "fOrMaT thIs aS titLe String" ) ,
1050
+ ( "Format,This-As*Title;String" , "fOrMaT,thIs-aS*titLe;String" ) ,
1051
+ ( "Getint" , "getInt" ) ,
1052
+ ( "Greek Ωppercases ..." , "greek ωppercases ..." ) ,
1053
+ ] ;
1054
+ for ( title, input) in tests {
1055
+ assert_eq ! ( PyString :: from( input) . title( & vm) . as_str( ) , title) ;
1056
+ }
1057
+ }
1058
+
1059
+ #[ test]
1060
+ fn str_istitle ( ) {
1061
+ let vm = VirtualMachine :: new ( ) ;
1062
+
1063
+ let pos = vec ! [
1064
+ "A" ,
1065
+ "A Titlecased Line" ,
1066
+ "A\n Titlecased Line" ,
1067
+ "A Titlecased, Line" ,
1068
+ "Greek Ωppercases ..." ,
1069
+ ] ;
1070
+
1071
+ for s in pos {
1072
+ assert ! ( PyString :: from( s) . istitle( & vm) ) ;
1073
+ }
1074
+
1075
+ let neg = vec ! [
1076
+ "" ,
1077
+ "a" ,
1078
+ "\n " ,
1079
+ "Not a capitalized String" ,
1080
+ "Not\t a Titlecase String" ,
1081
+ "Not--a Titlecase String" ,
1082
+ "NOT" ,
1083
+ ] ;
1084
+ for s in neg {
1085
+ assert ! ( !PyString :: from( s) . istitle( & vm) ) ;
999
1086
}
1000
1087
}
1001
- titled_str
1002
1088
}
0 commit comments