/
Casing.purs
197 lines (176 loc) · 6.11 KB
/
Casing.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
module Data.String.Casing
( toCamelCase
, toPascalCase
, toSnakeCase
, toScreamingSnakeCase
, toKebabCase
, toTitleCase
) where
import Prelude
import Data.Array (all, catMaybes, concat, elem, reverse, snoc, uncons, (:))
import Data.CodePoint.Unicode as CodePoint
import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..))
import Data.String.CodePoints (CodePoint, codePointFromChar, fromCodePointArray, toCodePointArray)
import Data.Tuple (Tuple(..))
stringToMaybe :: String -> Maybe String
stringToMaybe "" = Nothing
stringToMaybe value = Just value
mapHead :: forall a. (a -> a) -> Array a -> Array a
mapHead mapping arr = case uncons arr of
Just { head: x, tail: xs } -> mapping x : xs
Nothing -> arr
mapTail :: forall a. (a -> a) -> Array a -> Array a
mapTail mapping arr = case uncons arr of
Just { head: x, tail: xs } -> x : map mapping xs
Nothing -> arr
capitalize :: Array CodePoint -> Array CodePoint
capitalize = mapHead CodePoint.toUpperSimple <<< mapTail CodePoint.toLowerSimple
uncons2 :: forall a. Array a -> Maybe { x :: a, y :: a, rest :: Array a }
uncons2 arr = do
{ head: x, tail: xs } <- uncons arr
{ head: y, tail: rest } <- uncons xs
pure $ { x, y, rest }
isSeparator :: CodePoint -> Boolean
isSeparator codePoint = elem codePoint separators
where
separators = [ '_', '-', ' ' ] # map codePointFromChar
isBoundary :: CodePoint -> CodePoint -> Boolean
isBoundary _currentChar nextChar
| isSeparator nextChar = true
isBoundary currentChar nextChar = CodePoint.isLower currentChar && CodePoint.isUpper nextChar
getWords :: String -> Array String
getWords value = reverse $ catMaybes $ map stringToMaybe $ getWords' [] [] $ toCodePointArray value
where
getWords' :: Array CodePoint -> Array String -> Array CodePoint -> Array String
getWords' currentWord acc [] = fromCodePointArray currentWord : acc
getWords' currentWord acc [ singleChar ] = fromCodePointArray (currentWord `snoc` singleChar) : acc
getWords' currentWord acc chars = case uncons2 chars of
Just { x: currentChar, y: nextChar, rest: remainingChars } ->
let
appendCurrentChar :: Array CodePoint -> Array CodePoint
appendCurrentChar word =
if isSeparator currentChar then
word
else
concat [ word, [ currentChar ] ]
(Tuple currentWord' acc') =
if isBoundary currentChar nextChar then
Tuple "" (fromCodePointArray (appendCurrentChar currentWord) : acc)
else
if all CodePoint.isUpper currentWord
&& CodePoint.isUpper currentChar
&& CodePoint.isLower nextChar then
Tuple (fromCodePointArray $ appendCurrentChar []) (fromCodePointArray currentWord : acc)
else
Tuple (fromCodePointArray $ appendCurrentChar currentWord) acc
remainingChars' =
if not $ isSeparator nextChar then
nextChar : remainingChars
else
remainingChars
in
getWords' (toCodePointArray currentWord') acc' remainingChars'
Nothing -> acc
-- | Converts the given string to camelCase.
-- |
-- | In camelCase each word starts with an uppercase letter except for the first
-- | word, which starts with a lowercase letter.
-- |
-- | ```purescript
-- | toCamelCase "Hello World" == "helloWorld"
-- | toCamelCase "Player ID" == "playerId"
-- | toCamelCase "XMLHttpRequest" == "xmlHttpRequest"
-- | ```
toCamelCase :: String -> String
toCamelCase =
intercalate ""
<<< map fromCodePointArray
<<< mapTail capitalize
<<< mapHead (map CodePoint.toLowerSimple)
<<< map toCodePointArray
<<< getWords
-- | Converts the given string to PascalCase.
-- |
-- | In PascalCase the first letter of each word is uppercase.
-- |
-- | ```purescript
-- | toPascalCase "Hello World" == "HelloWorld"
-- | toPascalCase "Player ID" == "PlayerId"
-- | toPascalCase "XMLHttpRequest" == "XmlHttpRequest"
-- | ```
toPascalCase :: String -> String
toPascalCase =
intercalate ""
<<< map fromCodePointArray
<<< map capitalize
<<< map toCodePointArray
<<< getWords
-- | Converts the given string to snake_case.
-- |
-- | In snake_case all letters are lowercase and each word is separated by an
-- | underscore (`_`).
-- |
-- | ```purescript
-- | toSnakeCase "Hello World" == "hello_world"
-- | toSnakeCase "Player ID" == "player_id"
-- | toSnakeCase "XMLHttpRequest" == "xml_http_request"
-- | ```
toSnakeCase :: String -> String
toSnakeCase =
intercalate "_"
<<< map fromCodePointArray
<<< map (map CodePoint.toLowerSimple)
<<< map toCodePointArray
<<< getWords
-- | Converts the given string to SCREAMING_SNAKE_CASE.
-- |
-- | In SCREAMING_SNAKE_CASE all letters are uppercase and each word is separated
-- | by an underscore (`_`).
-- |
-- | ```purescript
-- | toScreamingSnakeCase "Hello World" == "HELLO_WORLD"
-- | toScreamingSnakeCase "Player ID" == "PLAYER_ID"
-- | toScreamingSnakeCase "XMLHttpRequest" == "XML_HTTP_REQUEST"
-- | ```
toScreamingSnakeCase :: String -> String
toScreamingSnakeCase =
intercalate "_"
<<< map fromCodePointArray
<<< map (map CodePoint.toUpperSimple)
<<< map toCodePointArray
<<< getWords
-- | Converts the given string to kebab-case.
-- |
-- | In kebab-case all letters are lowercase and each word is separated by a
-- | hyphen (`-`).
-- |
-- | ```purescript
-- | toKebabCase "Hello World" == "hello-world"
-- | toKebabCase "Player ID" == "player-id"
-- | toKebabCase "XMLHttpRequest" == "xml-http-request"
-- | ```
toKebabCase :: String -> String
toKebabCase =
intercalate "-"
<<< map fromCodePointArray
<<< map (map CodePoint.toLowerSimple)
<<< map toCodePointArray
<<< getWords
-- | Converts the given string to Title Case.
-- |
-- | In Title Case the first letter of each word is uppercase and each word is
-- | separated by a space (` `).
-- |
-- | ```purescript
-- | toTitleCase "Hello World" == "Hello World"
-- | toTitleCase "Player ID" == "Player Id"
-- | toTitleCase "XMLHttpRequest" == "Xml Http Request"
-- | ```
toTitleCase :: String -> String
toTitleCase =
intercalate " "
<<< map fromCodePointArray
<<< map capitalize
<<< map toCodePointArray
<<< getWords