-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathDataStores.hs
122 lines (104 loc) · 3.37 KB
/
DataStores.hs
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
module DataStores where
import Control.Concurrent
import Control.Concurrent.STM
import Data.Map as M hiding (map, filter)
import System.IO
data Room = Room {
roomName :: String,
users :: [User]
}
instance Eq Room where
r == q = (roomName r) == (roomName q)
data User = User {
userName :: String,
connection :: TMVar Handle,
rooms :: [Room]
}
instance Eq User where
u == p = (userName u) == (userName p)
class StringKey a where
stringKey :: a -> String
instance StringKey User where
stringKey = userName
instance StringKey Room where
stringKey = roomName
type UserStore = TVar (Map String User)
type RoomStore = TVar (Map String Room)
createRoomIfNeeded :: RoomStore ->
String ->
STM Room
createRoomIfNeeded roomStore name = do
roomStoreMap <- readTVar roomStore
case M.lookup name roomStoreMap of
Just existing -> return existing
Nothing -> do
let newRoom = Room {
roomName = name,
users = []
}
newMap = M.insert (roomName newRoom) newRoom roomStoreMap
writeTVar roomStore newMap
return newRoom
addUserToRoom :: UserStore ->
RoomStore ->
User ->
String ->
STM (User, Bool)
addUserToRoom userStore roomStore user roomName = do
room <- createRoomIfNeeded roomStore roomName
let newUser = (user { rooms = room : (rooms user) } )
newRoom = (room { users = user : (users room) } )
if not (user `elem` (users room)) -- only add if necessary
then do
updateSTM userStore newUser
updateSTM roomStore newRoom
return (newUser, True)
else return (user, False)
removeUserFromRoom :: UserStore ->
RoomStore ->
User ->
String ->
STM (User, Bool)
removeUserFromRoom userStore roomStore user roomName = do
room <- createRoomIfNeeded roomStore roomName
let newUser =
(user { rooms = filter (/= room) (rooms user) })
newRoom =
(room { users = filter (/= user) (users room) })
if user `elem` (users room)
then do
updateSTM userStore newUser
updateSTM roomStore newRoom
return (newUser, True)
else return (user, False)
removeUserFromRooms :: User ->
UserStore ->
RoomStore ->
STM ()
removeUserFromRooms user userStore roomStore = do
let userRooms = rooms user
-- this somewhat tricky. first creates a list of actions:
-- each one updates a room in STM to have the user removed. then,
-- use foldr to apply >> to each one to create a single again
-- which runs all of them in sequence.
foldr (>>) (return ()) $ map (\newRoom -> updateSTM roomStore newRoom) $
map (\r -> r {
users = filter (/= user) $ users r
}) userRooms
updateSTM :: (StringKey a) =>
TVar (Map String a) ->
a ->
STM ()
updateSTM store a = do
map <- readTVar store
let newMap = M.insert (stringKey a) a map
writeTVar store newMap
maybeGrabFromSTM :: TVar (Map String a) ->
String ->
STM (Maybe a)
maybeGrabFromSTM mapVar name = do
map <- readTVar mapVar
let maybeObj = M.lookup name map in
case maybeObj `seq` maybeObj of
Just a -> return (return a)
Nothing -> return Nothing