]> git.lizzy.rs Git - go-anidb.git/blob - usercache.go
anidb: Parse the LID from FILE response
[go-anidb.git] / usercache.go
1 package anidb
2
3 import (
4         "github.com/Kovensky/go-anidb/udp"
5         "github.com/Kovensky/go-fscache"
6         "strconv"
7         "strings"
8         "sync"
9         "time"
10 )
11
12 type UID int
13
14 func (adb *AniDB) GetCurrentUser() <-chan *User {
15         ch := make(chan *User, 1)
16
17         if adb.udp.credentials == nil {
18                 ch <- nil
19                 close(ch)
20                 return ch
21         }
22
23         return adb.GetUserByName(decrypt(adb.udp.credentials.username))
24 }
25
26 // This is an (almost) entirely local representation.
27 func (adb *AniDB) GetUserByID(uid UID) <-chan *User {
28         key := []fscache.CacheKey{"user", uid}
29         ch := make(chan *User, 1)
30
31         if uid < 1 {
32                 ch <- nil
33                 close(ch)
34                 return ch
35         }
36
37         ic := make(chan notification, 1)
38         go func() { ch <- (<-ic).(*User); close(ch) }()
39         if intentMap.Intent(ic, key...) {
40                 return ch
41         }
42
43         if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
44                 intentMap.NotifyClose((*User)(nil), key...)
45                 return ch
46         }
47
48         go func() {
49                 var user *User
50                 if CacheGet(&user, key...) == nil {
51                         intentMap.NotifyClose(user, key...)
52                         return
53                 }
54                 <-adb.GetUserName(uid)
55
56                 CacheGet(&user, key...)
57                 intentMap.NotifyClose(user)
58         }()
59         return ch
60 }
61
62 func (adb *AniDB) GetUserByName(username string) <-chan *User {
63         ch := make(chan *User, 1)
64
65         if username == "" {
66                 ch <- nil
67                 close(ch)
68                 return ch
69         }
70
71         go func() {
72                 ch <- <-adb.GetUserByID(<-adb.GetUserUID(username))
73                 close(ch)
74         }()
75         return ch
76 }
77
78 func (adb *AniDB) GetUserUID(username string) <-chan UID {
79         key := []fscache.CacheKey{"user", "by-name", username}
80         ch := make(chan UID, 1)
81
82         if username == "" {
83                 ch <- 0
84                 close(ch)
85                 return ch
86         }
87
88         ic := make(chan notification, 1)
89         go func() { ch <- (<-ic).(UID); close(ch) }()
90         if intentMap.Intent(ic, key...) {
91                 return ch
92         }
93
94         if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
95                 intentMap.NotifyClose((UID)(0), key...)
96                 return ch
97         }
98
99         uid := UID(0)
100         switch ts, err := Cache.Get(&uid, key...); {
101         case err == nil && time.Now().Sub(ts) < UIDCacheDuration:
102                 intentMap.NotifyClose(uid, key...)
103                 return ch
104         }
105
106         go func() {
107                 reply := <-adb.udp.SendRecv("USER",
108                         paramMap{"user": username})
109
110                 switch reply.Code() {
111                 case 295:
112                         uid, _ = parseUserReply(reply) // caches
113                 case 394:
114                         Cache.SetInvalid(key...)
115                 }
116
117                 intentMap.NotifyClose(uid, key...)
118         }()
119         return ch
120 }
121
122 func (adb *AniDB) GetUserName(uid UID) <-chan string {
123         key := []fscache.CacheKey{"user", "by-uid", uid}
124         ch := make(chan string, 1)
125
126         if uid < 1 {
127                 ch <- ""
128                 close(ch)
129                 return ch
130         }
131
132         ic := make(chan notification, 1)
133         go func() { ch <- (<-ic).(string); close(ch) }()
134         if intentMap.Intent(ic, key...) {
135                 return ch
136         }
137
138         if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
139                 intentMap.NotifyClose("", key...)
140                 return ch
141         }
142
143         name := ""
144         switch ts, err := Cache.Get(&name, key...); {
145         case err == nil && time.Now().Sub(ts) < UIDCacheDuration:
146                 intentMap.NotifyClose(name, key...)
147                 return ch
148         }
149
150         go func() {
151                 reply := <-adb.udp.SendRecv("USER",
152                         paramMap{"uid": uid})
153
154                 switch reply.Code() {
155                 case 295:
156                         _, name = parseUserReply(reply) // caches
157                 case 394:
158                         Cache.SetInvalid(key...)
159                 }
160
161                 intentMap.NotifyClose(name, key...)
162         }()
163         return ch
164 }
165
166 var userReplyMutex sync.Mutex
167
168 func parseUserReply(reply udpapi.APIReply) (UID, string) {
169         userReplyMutex.Lock()
170         defer userReplyMutex.Unlock()
171
172         if reply.Error() == nil {
173                 parts := strings.Split(reply.Lines()[1], "|")
174                 id, _ := strconv.ParseInt(parts[0], 10, 32)
175
176                 CacheSet(UID(id), "user", "by-name", parts[1])
177                 CacheSet(parts[1], "user", "by-uid", id)
178
179                 if _, err := Cache.Stat("user", id); err != nil {
180                         CacheSet(&User{
181                                 UID:      UID(id),
182                                 Username: parts[1],
183                         }, "user", id)
184                 }
185
186                 return UID(id), parts[1]
187         }
188         return 0, ""
189 }