github.com/simpleiot/simpleiot@v0.18.3/frontend/src/UI/Sanitize.elm (about) 1 module UI.Sanitize exposing (date, float, hmParser, parseDate, parseHM, time) 2 3 import Parser exposing ((|.), Parser) 4 5 6 float : String -> String 7 float input = 8 Tuple.second <| floatHelper ( input, "" ) 9 10 11 floatHelper : ( String, String ) -> ( String, String ) 12 floatHelper state = 13 let 14 sIn = 15 Tuple.first state 16 17 sInList = 18 String.toList sIn 19 in 20 case sInList of 21 c :: rest -> 22 let 23 sOut = 24 Tuple.second state 25 in 26 if (String.length sOut == 0 && c == '-') || Char.isDigit c || (c == '.' && not (String.contains "." sOut)) then 27 let 28 sOutList = 29 String.toList sOut 30 in 31 floatHelper ( String.fromList rest, String.fromList <| sOutList ++ [ c ] ) 32 33 else 34 state 35 36 _ -> 37 state 38 39 40 {-| sanitizeTime looks for a valid time input in the form of hh:mm 41 this function simply stops when it hits an invalid 42 portion and returns the valid portion 43 -} 44 time : String -> String 45 time t = 46 Tuple.second <| timeHelper ( t, "" ) 47 48 49 {-| -- the first value in tuple is incoming string, and 2nd is output 50 we chomp characters from the input and then process them to the output 51 -} 52 timeHelper : ( String, String ) -> ( String, String ) 53 timeHelper state = 54 let 55 sIn = 56 Tuple.first state 57 58 sInList = 59 String.toList sIn 60 in 61 case sInList of 62 c :: rest -> 63 let 64 sOut = 65 Tuple.second state 66 67 sOutList = 68 String.toList sOut 69 70 checkDigit = 71 \ch chRest -> 72 if Char.isDigit ch then 73 timeHelper ( String.fromList chRest, String.fromList <| sOutList ++ [ ch ] ) 74 75 else 76 ( "", sOut ) 77 in 78 case List.length sOutList of 79 0 -> 80 checkDigit c rest 81 82 1 -> 83 if c == ':' then 84 -- add a leading digit and try again 85 timeHelper ( sIn, String.fromList <| '0' :: sOutList ) 86 87 else if Char.isDigit c then 88 let 89 sOutNew = 90 String.fromList <| sOutList ++ [ c ] 91 in 92 case String.toInt (String.slice 0 2 sOutNew) of 93 Just hr -> 94 if hr > 23 then 95 ( "", sOut ) 96 97 else 98 timeHelper ( String.fromList rest, sOutNew ) 99 100 Nothing -> 101 ( "", sOut ) 102 103 else 104 ( "", sOut ) 105 106 2 -> 107 if c == ':' then 108 timeHelper ( String.fromList rest, String.fromList <| sOutList ++ [ c ] ) 109 110 else 111 ( "", sOut ) 112 113 3 -> 114 checkDigit c rest 115 116 4 -> 117 if Char.isDigit c then 118 let 119 sOutNew = 120 String.fromList <| sOutList ++ [ c ] 121 in 122 case String.toInt (String.slice 3 5 sOutNew) of 123 Just hr -> 124 if hr > 59 then 125 ( "", sOut ) 126 127 else 128 timeHelper ( String.fromList rest, sOutNew ) 129 130 Nothing -> 131 ( "", sOut ) 132 133 else 134 ( "", sOut ) 135 136 _ -> 137 ( "", sOut ) 138 139 _ -> 140 -- we are done 141 state 142 143 144 parseHM : String -> Maybe String 145 parseHM t = 146 Parser.run hmParser t 147 |> Result.toMaybe 148 149 150 hmParser : Parser String 151 hmParser = 152 Parser.getChompedString <| 153 Parser.succeed identity 154 |. (Parser.oneOf [ Parser.backtrackable altIntParser, Parser.int ] 155 |> Parser.andThen 156 (\v -> 157 if v < 0 || v > 23 then 158 Parser.problem "hour is out of range" 159 160 else 161 Parser.succeed v 162 ) 163 ) 164 |. Parser.symbol ":" 165 |. ((Parser.oneOf [ altIntParser, Parser.int ] 166 |> Parser.andThen 167 (\v -> 168 if v < 0 || v > 59 then 169 Parser.problem "minute is not in range" 170 171 else 172 Parser.succeed v 173 ) 174 ) 175 |> Parser.getChompedString 176 |> Parser.andThen 177 (\s -> 178 if String.length s /= 2 then 179 Parser.problem "minute must be 2 digits" 180 181 else 182 Parser.succeed s 183 ) 184 ) 185 186 187 parseDate : String -> Maybe String 188 parseDate d = 189 Parser.run dateParser d 190 |> Result.toMaybe 191 192 193 dateParser : Parser String 194 dateParser = 195 Parser.getChompedString <| 196 Parser.succeed identity 197 |. (Parser.oneOf [ Parser.backtrackable altIntParser, Parser.int ] 198 |> Parser.andThen 199 (\v -> 200 if v < 2023 || v > 2099 then 201 Parser.problem "year is out of range" 202 203 else 204 Parser.succeed v 205 ) 206 ) 207 |. Parser.symbol "-" 208 |. ((Parser.oneOf [ altIntParser, Parser.int ] 209 |> Parser.andThen 210 (\v -> 211 if v < 1 || v > 12 then 212 Parser.problem "month is not in range" 213 214 else 215 Parser.succeed v 216 ) 217 ) 218 |> Parser.getChompedString 219 |> Parser.andThen 220 (\s -> 221 if String.length s /= 2 then 222 Parser.problem "month must be 2 digits" 223 224 else 225 Parser.succeed s 226 ) 227 ) 228 |. Parser.symbol "-" 229 |. ((Parser.oneOf [ altIntParser, Parser.int ] 230 |> Parser.andThen 231 (\v -> 232 if v < 1 || v > 31 then 233 Parser.problem "day is not in range" 234 235 else 236 Parser.succeed v 237 ) 238 ) 239 |> Parser.getChompedString 240 |> Parser.andThen 241 (\s -> 242 if String.length s /= 2 then 243 Parser.problem "day must be 2 digits" 244 245 else 246 Parser.succeed s 247 ) 248 ) 249 250 251 altIntParser : Parser Int 252 altIntParser = 253 Parser.symbol "0" |> Parser.andThen (\_ -> Parser.int) 254 255 256 {-| date looks for a valid date input in the form of YYYY-MM-DD 257 this function simply stops when it hits an invalid 258 portion and returns the valid portion 259 -} 260 date : String -> String 261 date d = 262 Tuple.second <| dateHelper ( d, "" ) 263 264 265 {-| -- the first value in tuple is incoming string, and 2nd is output 266 we chomp characters from the input and then process them to the output 267 0123456789 268 YYYY-MM-DD 269 -} 270 dateHelper : ( String, String ) -> ( String, String ) 271 dateHelper state = 272 let 273 sIn = 274 Tuple.first state 275 276 sInList = 277 String.toList sIn 278 in 279 case sInList of 280 c :: rest -> 281 let 282 sOut = 283 Tuple.second state 284 285 sOutList = 286 String.toList sOut 287 288 checkDigit = 289 \ch chRest -> 290 if Char.isDigit ch then 291 dateHelper ( String.fromList chRest, String.fromList <| sOutList ++ [ ch ] ) 292 293 else 294 ( "", sOut ) 295 in 296 case List.length sOutList of 297 0 -> 298 if c /= '2' then 299 ( "", sOut ) 300 301 else 302 checkDigit c rest 303 304 1 -> 305 if c /= '0' then 306 ( "", sOut ) 307 308 else 309 checkDigit c rest 310 311 2 -> 312 checkDigit c rest 313 314 3 -> 315 checkDigit c rest 316 317 4 -> 318 if c == '-' then 319 dateHelper ( String.fromList rest, String.fromList <| sOutList ++ [ c ] ) 320 321 else 322 ( "", sOut ) 323 324 5 -> 325 checkDigit c rest 326 327 6 -> 328 if c == '-' then 329 dateHelper 330 ( sIn 331 , String.fromList <| 332 List.take 5 sOutList 333 ++ '0' 334 :: List.drop 5 sOutList 335 ) 336 337 else if Char.isDigit c then 338 let 339 sOutNew = 340 String.fromList <| sOutList ++ [ c ] 341 in 342 case String.toInt (String.slice 5 7 sOutNew) of 343 Just mo -> 344 if mo > 12 then 345 ( "", sOut ) 346 347 else 348 dateHelper ( String.fromList rest, sOutNew ) 349 350 Nothing -> 351 ( "", sOut ) 352 353 else 354 ( "", sOut ) 355 356 7 -> 357 if c == '-' then 358 dateHelper ( String.fromList rest, String.fromList <| sOutList ++ [ c ] ) 359 360 else 361 ( "", sOut ) 362 363 8 -> 364 checkDigit c rest 365 366 9 -> 367 if Char.isDigit c then 368 let 369 sOutNew = 370 String.fromList <| sOutList ++ [ c ] 371 in 372 case String.toInt (String.slice 8 10 sOutNew) of 373 Just day -> 374 if day > 31 then 375 ( "", sOut ) 376 377 else 378 dateHelper ( String.fromList rest, sOutNew ) 379 380 Nothing -> 381 ( "", sOut ) 382 383 else 384 ( "", sOut ) 385 386 _ -> 387 ( "", sOut ) 388 389 _ -> 390 -- we are done 391 state