conversion.odin 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. package strings
  2. import "core:io"
  3. import "core:unicode"
  4. import "core:unicode/utf8"
  5. to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
  6. if len(s) == 0 {
  7. return ""
  8. }
  9. b: Builder
  10. init_builder(&b, 0, 0, allocator)
  11. s := s
  12. for c, i in s {
  13. if c != utf8.RUNE_ERROR {
  14. continue
  15. }
  16. _, w := utf8.decode_rune_in_string(s[i:])
  17. if w == 1 {
  18. grow_builder(&b, len(s) + len(replacement))
  19. write_string(&b, s[:i])
  20. s = s[i:]
  21. break
  22. }
  23. }
  24. if builder_cap(b) == 0 {
  25. return clone(s, allocator)
  26. }
  27. invalid := false
  28. for i := 0; i < len(s); /**/ {
  29. c := s[i]
  30. if c < utf8.RUNE_SELF {
  31. i += 1
  32. invalid = false
  33. write_byte(&b, c)
  34. continue
  35. }
  36. _, w := utf8.decode_rune_in_string(s[i:])
  37. if w == 1 {
  38. i += 1
  39. if !invalid {
  40. invalid = true
  41. write_string(&b, replacement)
  42. }
  43. continue
  44. }
  45. invalid = false
  46. write_string(&b, s[i:][:w])
  47. i += w
  48. }
  49. return to_string(b)
  50. }
  51. to_lower :: proc(s: string, allocator := context.allocator) -> string {
  52. b: Builder
  53. init_builder(&b, 0, len(s), allocator)
  54. for r in s {
  55. write_rune_builder(&b, unicode.to_lower(r))
  56. }
  57. return to_string(b)
  58. }
  59. to_upper :: proc(s: string, allocator := context.allocator) -> string {
  60. b: Builder
  61. init_builder(&b, 0, len(s), allocator)
  62. for r in s {
  63. write_rune_builder(&b, unicode.to_upper(r))
  64. }
  65. return to_string(b)
  66. }
  67. is_delimiter :: proc(c: rune) -> bool {
  68. return c == '-' || c == '_' || is_space(c)
  69. }
  70. is_separator :: proc(r: rune) -> bool {
  71. if r <= 0x7f {
  72. switch r {
  73. case '0'..='9': return false
  74. case 'a'..='z': return false
  75. case 'A'..='Z': return false
  76. case '_': return false
  77. }
  78. return true
  79. }
  80. // TODO(bill): unicode categories
  81. // if unicode.is_letter(r) || unicode.is_digit(r) {
  82. // return false;
  83. // }
  84. return unicode.is_space(r)
  85. }
  86. string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
  87. prev, curr: rune
  88. for next in s {
  89. if curr == 0 {
  90. prev = curr
  91. curr = next
  92. continue
  93. }
  94. callback(w, prev, curr, next)
  95. prev = curr
  96. curr = next
  97. }
  98. if len(s) > 0 {
  99. callback(w, prev, curr, 0)
  100. }
  101. }
  102. to_lower_camel_case :: to_camel_case
  103. to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
  104. s := s
  105. s = trim_space(s)
  106. b: Builder
  107. init_builder(&b, 0, len(s), allocator)
  108. w := to_writer(&b)
  109. string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
  110. if !is_delimiter(curr) {
  111. if is_delimiter(prev) {
  112. io.write_rune(w, unicode.to_upper(curr))
  113. } else if unicode.is_lower(prev) {
  114. io.write_rune(w, curr)
  115. } else {
  116. io.write_rune(w, unicode.to_lower(curr))
  117. }
  118. }
  119. })
  120. return to_string(b)
  121. }
  122. to_upper_camel_case :: to_pascal_case
  123. to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
  124. s := s
  125. s = trim_space(s)
  126. b: Builder
  127. init_builder(&b, 0, len(s), allocator)
  128. w := to_writer(&b)
  129. string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
  130. if !is_delimiter(curr) {
  131. if is_delimiter(prev) || prev == 0 {
  132. io.write_rune(w, unicode.to_upper(curr))
  133. } else if unicode.is_lower(prev) {
  134. io.write_rune(w, curr)
  135. } else {
  136. io.write_rune(w, unicode.to_lower(curr))
  137. }
  138. }
  139. })
  140. return to_string(b)
  141. }
  142. to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
  143. s := s
  144. s = trim_space(s)
  145. b: Builder
  146. init_builder(&b, 0, len(s), allocator)
  147. w := to_writer(&b)
  148. adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower
  149. prev, curr: rune
  150. for next in s {
  151. if is_delimiter(curr) {
  152. if !is_delimiter(prev) {
  153. io.write_rune(w, delimiter)
  154. }
  155. } else if unicode.is_upper(curr) {
  156. if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
  157. io.write_rune(w, delimiter)
  158. }
  159. io.write_rune(w, adjust_case(curr))
  160. } else if curr != 0 {
  161. io.write_rune(w, adjust_case(curr))
  162. }
  163. prev = curr
  164. curr = next
  165. }
  166. if len(s) > 0 {
  167. if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
  168. io.write_rune(w, delimiter)
  169. }
  170. io.write_rune(w, adjust_case(curr))
  171. }
  172. return to_string(b)
  173. }
  174. to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
  175. return to_delimiter_case(s, '_', false, allocator)
  176. }
  177. to_screaming_snake_case :: to_upper_snake_case
  178. to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
  179. return to_delimiter_case(s, '_', true, allocator)
  180. }
  181. to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
  182. return to_delimiter_case(s, '-', false, allocator)
  183. }
  184. to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
  185. return to_delimiter_case(s, '-', true, allocator)
  186. }
  187. to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
  188. delimiter :: '_'
  189. s := s
  190. s = trim_space(s)
  191. b: Builder
  192. init_builder(&b, 0, len(s), allocator)
  193. w := to_writer(&b)
  194. prev, curr: rune
  195. for next in s {
  196. if is_delimiter(curr) {
  197. if !is_delimiter(prev) {
  198. io.write_rune(w, delimiter)
  199. }
  200. } else if unicode.is_upper(curr) {
  201. if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
  202. io.write_rune(w, delimiter)
  203. }
  204. io.write_rune(w, unicode.to_upper(curr))
  205. } else if curr != 0 {
  206. io.write_rune(w, unicode.to_lower(curr))
  207. }
  208. prev = curr
  209. curr = next
  210. }
  211. if len(s) > 0 {
  212. if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
  213. io.write_rune(w, delimiter)
  214. io.write_rune(w, unicode.to_upper(curr))
  215. } else {
  216. io.write_rune(w, unicode.to_lower(curr))
  217. }
  218. }
  219. return to_string(b)
  220. }