-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathKey.elm
More file actions
91 lines (75 loc) · 2.72 KB
/
Key.elm
File metadata and controls
91 lines (75 loc) · 2.72 KB
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
effect module Key where { command = MgrCmd } exposing (generate)
import Task exposing (Task)
import Random
import Random.String as Random
import Time
{-|
**WARNING** Do not call this multiple times with the same seed! It will
generate identical keys. Please use `generateKeys` instead to aquire
multiple unique ordered keys.
For those of you thinking you can initialize the seet from the time we pass
in **DO NOT DO IT**. If you examine the
[original firebase code](https://gist.github.com/mikelehen/3596a30bd69384624c11)
you will notice that they have a duplicate time check. We can safely
avoid that check (and since elm's Random implementation is deterministic
we have to!) by using as seed initialized at a time independent of the
time the key is generated at.
--}
generateKey : Random.Seed -> Task Never (String, Random.Seed)
generateKey seed =
let
(randString, newSeed) = Random.step (Random.string 12 charGenerator) seed
pred time =
(timeString time ++ randString, newSeed)
in
Task.map pred Time.now
pool : String
pool =
"-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
charInPool : Int -> Char
charInPool index =
String.slice index (index + 1) pool
|> String.uncons
|> Maybe.map Tuple.first
|> Maybe.withDefault '*'
charGenerator : Random.Generator Char
charGenerator =
Random.map charInPool <| Random.int 0 63
timeString : Float -> String
timeString time =
let
timeBase64 power =
floor (time / (64 ^ toFloat power)) % 64
in
List.range 0 7
|> List.map (timeBase64 >> charInPool)
|> List.reverse
|> String.fromList
-- EFFECT MANAGER
type MgrCmd msg = Generate (String -> msg)
type alias State =
Random.Seed
generate : (String -> msg) -> Cmd msg
generate =
command << Generate
cmdMap : (a -> b) -> MgrCmd a -> MgrCmd b
cmdMap func (Generate a) =
Generate <| func << a
init : Task Never State
init =
Task.map (Debug.log "initialized" << Random.initialSeed << round) Time.now
onEffects : Platform.Router msg Never -> List (MgrCmd msg) -> State -> Task Never State
onEffects router commands seed =
case commands of
[] ->
Task.succeed seed
Generate tagger :: rest ->
let
route (key, newSeed) =
Platform.sendToApp router (tagger key)
|> Task.andThen (\_ -> onEffects router rest (Debug.log "new seed persisted" newSeed))
in
Task.andThen route <| generateKey seed
onSelfMsg : Platform.Router msg Never -> Never -> State -> Task Never State
onSelfMsg _ _ seed =
Debug.log "onSelfMsg" <| Task.succeed seed