github.com/diamondburned/arikawa/v2@v2.1.0/state/store/store.go (about) 1 // Package store contains interfaces of the state's storage and its 2 // implementations. 3 // 4 // Getter Methods 5 // 6 // All getter methods will be wrapped by the State. If the State can't find 7 // anything in the storage, it will call the API itself and automatically add 8 // what's missing into the storage. 9 // 10 // Methods that return with a slice should pay attention to race conditions that 11 // would mutate the underlying slice (and as a result the returned slice as 12 // well). The best way to avoid this is to copy the whole slice, like 13 // defaultstore implementations do. 14 // 15 // Getter methods should not care about returning slices in order, unless 16 // explicitly stated against. 17 // 18 // ErrNotFound Rules 19 // 20 // If a getter method cannot find something, it should return ErrNotFound. 21 // Callers including State may check if the error is ErrNotFound to do something 22 // else. For example, if Guilds currently stores nothing, then it should return 23 // an empty slice and a nil error. 24 // 25 // In some cases, there may not be a way to know whether or not the store is 26 // unpopulated or is actually empty. In that case, implementations can return 27 // ErrNotFound when either happens. This will make State refetch from the API, 28 // so it is not ideal. 29 // 30 // Remove Methods 31 // 32 // Remove methods should return a nil error if the item it wants to delete is 33 // not found. This helps save some additional work in some cases. 34 package store 35 36 import ( 37 "errors" 38 "fmt" 39 40 "github.com/diamondburned/arikawa/v2/discord" 41 "github.com/diamondburned/arikawa/v2/gateway" 42 ) 43 44 // ErrNotFound is an error that a store can use to return when something isn't 45 // in the storage. There is no strict restrictions on what uses this (the 46 // default one does, though), so be advised. 47 var ErrNotFound = errors.New("item not found in store") 48 49 // Cabinet combines all store interfaces into one but allows swapping individual 50 // stores out for another. Since the struct only consists of interfaces, it can 51 // be copied around. 52 type Cabinet struct { 53 MeStore 54 ChannelStore 55 EmojiStore 56 GuildStore 57 MemberStore 58 MessageStore 59 PresenceStore 60 RoleStore 61 VoiceStateStore 62 } 63 64 // Reset resets everything inside the container. 65 func (sc *Cabinet) Reset() error { 66 errors := []error{ 67 sc.MeStore.Reset(), 68 sc.ChannelStore.Reset(), 69 sc.EmojiStore.Reset(), 70 sc.GuildStore.Reset(), 71 sc.MemberStore.Reset(), 72 sc.MessageStore.Reset(), 73 sc.PresenceStore.Reset(), 74 sc.RoleStore.Reset(), 75 sc.VoiceStateStore.Reset(), 76 } 77 78 nonNils := errors[:0] 79 80 for _, err := range errors { 81 if err != nil { 82 nonNils = append(nonNils, err) 83 } 84 } 85 86 if len(nonNils) > 0 { 87 return ResetErrors(nonNils) 88 } 89 90 return nil 91 } 92 93 // ResetErrors represents the multiple errors when StoreContainer is being 94 // resetted. A ResetErrors value must have at least 1 error. 95 type ResetErrors []error 96 97 // Error formats ResetErrors, showing the number of errors and the last error. 98 func (errs ResetErrors) Error() string { 99 return fmt.Sprintf( 100 "encountered %d reset errors (last: %v)", 101 len(errs), errs[len(errs)-1], 102 ) 103 } 104 105 // Unwrap returns the last error in the list. 106 func (errs ResetErrors) Unwrap() error { 107 return errs[len(errs)-1] 108 } 109 110 // append adds the error only if it is not nil. 111 func (errs *ResetErrors) append(err error) { 112 if err != nil { 113 *errs = append(*errs, err) 114 } 115 } 116 117 // Noop is the value for a NoopStore. 118 var Noop = NoopStore{} 119 120 // Noop is a no-op implementation of all store interfaces. Its getters will 121 // always return ErrNotFound, and its setters will never return an error. 122 type NoopStore = noop 123 124 // NoopCabinet is a store cabinet with all store methods set to the Noop 125 // implementations. 126 var NoopCabinet = Cabinet{ 127 MeStore: Noop, 128 ChannelStore: Noop, 129 EmojiStore: Noop, 130 GuildStore: Noop, 131 MemberStore: Noop, 132 MessageStore: Noop, 133 PresenceStore: Noop, 134 RoleStore: Noop, 135 VoiceStateStore: Noop, 136 } 137 138 // noop is the Noop type that implements methods. 139 type noop struct{} 140 141 // Resetter is an interface to reset the store on every Ready event. 142 type Resetter interface { 143 // Reset resets the store to a new valid instance. 144 Reset() error 145 } 146 147 var _ Resetter = (*noop)(nil) 148 149 func (noop) Reset() error { return nil } 150 151 // MeStore is the store interface for the current user. 152 type MeStore interface { 153 Resetter 154 155 Me() (*discord.User, error) 156 MyselfSet(me discord.User) error 157 } 158 159 func (noop) Me() (*discord.User, error) { return nil, ErrNotFound } 160 func (noop) MyselfSet(discord.User) error { return nil } 161 162 // ChannelStore is the store interface for all channels. 163 type ChannelStore interface { 164 Resetter 165 166 // ChannelStore searches for both DM and guild channels. 167 Channel(discord.ChannelID) (*discord.Channel, error) 168 // CreatePrivateChannelStore searches for private channels by the recipient ID. 169 // It has the same API as *api.Client does. 170 CreatePrivateChannel(recipient discord.UserID) (*discord.Channel, error) 171 172 // Channels returns only channels from a guild. 173 Channels(discord.GuildID) ([]discord.Channel, error) 174 // PrivateChannels returns all private channels from the state. 175 PrivateChannels() ([]discord.Channel, error) 176 177 // Both ChannelSet and ChannelRemove should switch on Type to know if it's a 178 // private channel or not. 179 180 ChannelSet(discord.Channel) error 181 ChannelRemove(discord.Channel) error 182 } 183 184 var _ ChannelStore = (*noop)(nil) 185 186 func (noop) Channel(discord.ChannelID) (*discord.Channel, error) { 187 return nil, ErrNotFound 188 } 189 func (noop) CreatePrivateChannel(discord.UserID) (*discord.Channel, error) { 190 return nil, ErrNotFound 191 } 192 func (noop) Channels(discord.GuildID) ([]discord.Channel, error) { 193 return nil, ErrNotFound 194 } 195 func (noop) PrivateChannels() ([]discord.Channel, error) { 196 return nil, ErrNotFound 197 } 198 func (noop) ChannelSet(discord.Channel) error { 199 return nil 200 } 201 func (noop) ChannelRemove(discord.Channel) error { 202 return nil 203 } 204 205 // EmojiStore is the store interface for all emojis. 206 type EmojiStore interface { 207 Resetter 208 209 Emoji(discord.GuildID, discord.EmojiID) (*discord.Emoji, error) 210 Emojis(discord.GuildID) ([]discord.Emoji, error) 211 212 // EmojiSet should delete all old emojis before setting new ones. The given 213 // emojis slice will be a complete list of all emojis. 214 EmojiSet(discord.GuildID, []discord.Emoji) error 215 } 216 217 var _ EmojiStore = (*noop)(nil) 218 219 func (noop) Emoji(discord.GuildID, discord.EmojiID) (*discord.Emoji, error) { 220 return nil, ErrNotFound 221 } 222 func (noop) Emojis(discord.GuildID) ([]discord.Emoji, error) { 223 return nil, ErrNotFound 224 } 225 func (noop) EmojiSet(discord.GuildID, []discord.Emoji) error { 226 return nil 227 } 228 229 // GuildStore is the store interface for all guilds. 230 type GuildStore interface { 231 Resetter 232 233 Guild(discord.GuildID) (*discord.Guild, error) 234 Guilds() ([]discord.Guild, error) 235 236 GuildSet(discord.Guild) error 237 GuildRemove(id discord.GuildID) error 238 } 239 240 var _ GuildStore = (*noop)(nil) 241 242 func (noop) Guild(discord.GuildID) (*discord.Guild, error) { return nil, ErrNotFound } 243 func (noop) Guilds() ([]discord.Guild, error) { return nil, ErrNotFound } 244 func (noop) GuildSet(discord.Guild) error { return nil } 245 func (noop) GuildRemove(discord.GuildID) error { return nil } 246 247 // MemberStore is the store interface for all members. 248 type MemberStore interface { 249 Resetter 250 251 Member(discord.GuildID, discord.UserID) (*discord.Member, error) 252 Members(discord.GuildID) ([]discord.Member, error) 253 254 MemberSet(discord.GuildID, discord.Member) error 255 MemberRemove(discord.GuildID, discord.UserID) error 256 } 257 258 var _ MemberStore = (*noop)(nil) 259 260 func (noop) Member(discord.GuildID, discord.UserID) (*discord.Member, error) { 261 return nil, ErrNotFound 262 } 263 func (noop) Members(discord.GuildID) ([]discord.Member, error) { 264 return nil, ErrNotFound 265 } 266 func (noop) MemberSet(discord.GuildID, discord.Member) error { 267 return nil 268 } 269 func (noop) MemberRemove(discord.GuildID, discord.UserID) error { 270 return nil 271 } 272 273 // MessageStore is the store interface for all messages. 274 type MessageStore interface { 275 Resetter 276 277 // MaxMessages returns the maximum number of messages. It is used to know if 278 // the state cache is filled or not for one channel 279 MaxMessages() int 280 281 Message(discord.ChannelID, discord.MessageID) (*discord.Message, error) 282 // Messages should return messages ordered from latest to earliest. 283 Messages(discord.ChannelID) ([]discord.Message, error) 284 285 // MessageSet should prepend messages into the slice, the latest being in 286 // front. 287 MessageSet(discord.Message) error 288 MessageRemove(discord.ChannelID, discord.MessageID) error 289 } 290 291 var _ MessageStore = (*noop)(nil) 292 293 func (noop) MaxMessages() int { 294 return 0 295 } 296 func (noop) Message(discord.ChannelID, discord.MessageID) (*discord.Message, error) { 297 return nil, ErrNotFound 298 } 299 func (noop) Messages(discord.ChannelID) ([]discord.Message, error) { 300 return nil, ErrNotFound 301 } 302 func (noop) MessageSet(discord.Message) error { 303 return nil 304 } 305 func (noop) MessageRemove(discord.ChannelID, discord.MessageID) error { 306 return nil 307 } 308 309 // PresenceStore is the store interface for all user presences. Presences don't get 310 // fetched from the API; they will only be updated through the Gateway. 311 type PresenceStore interface { 312 Resetter 313 314 Presence(discord.GuildID, discord.UserID) (*gateway.Presence, error) 315 Presences(discord.GuildID) ([]gateway.Presence, error) 316 317 PresenceSet(discord.GuildID, gateway.Presence) error 318 PresenceRemove(discord.GuildID, discord.UserID) error 319 } 320 321 var _ PresenceStore = (*noop)(nil) 322 323 func (noop) Presence(discord.GuildID, discord.UserID) (*gateway.Presence, error) { 324 return nil, ErrNotFound 325 } 326 func (noop) Presences(discord.GuildID) ([]gateway.Presence, error) { 327 return nil, ErrNotFound 328 } 329 func (noop) PresenceSet(discord.GuildID, gateway.Presence) error { 330 return nil 331 } 332 func (noop) PresenceRemove(discord.GuildID, discord.UserID) error { 333 return nil 334 } 335 336 // RoleStore is the store interface for all member roles. 337 type RoleStore interface { 338 Resetter 339 340 Role(discord.GuildID, discord.RoleID) (*discord.Role, error) 341 Roles(discord.GuildID) ([]discord.Role, error) 342 343 RoleSet(discord.GuildID, discord.Role) error 344 RoleRemove(discord.GuildID, discord.RoleID) error 345 } 346 347 var _ RoleStore = (*noop)(nil) 348 349 func (noop) Role(discord.GuildID, discord.RoleID) (*discord.Role, error) { return nil, ErrNotFound } 350 func (noop) Roles(discord.GuildID) ([]discord.Role, error) { return nil, ErrNotFound } 351 func (noop) RoleSet(discord.GuildID, discord.Role) error { return nil } 352 func (noop) RoleRemove(discord.GuildID, discord.RoleID) error { return nil } 353 354 // VoiceStateStore is the store interface for all voice states. 355 type VoiceStateStore interface { 356 Resetter 357 358 VoiceState(discord.GuildID, discord.UserID) (*discord.VoiceState, error) 359 VoiceStates(discord.GuildID) ([]discord.VoiceState, error) 360 361 VoiceStateSet(discord.GuildID, discord.VoiceState) error 362 VoiceStateRemove(discord.GuildID, discord.UserID) error 363 } 364 365 var _ VoiceStateStore = (*noop)(nil) 366 367 func (noop) VoiceState(discord.GuildID, discord.UserID) (*discord.VoiceState, error) { 368 return nil, ErrNotFound 369 } 370 func (noop) VoiceStates(discord.GuildID) ([]discord.VoiceState, error) { 371 return nil, ErrNotFound 372 } 373 func (noop) VoiceStateSet(discord.GuildID, discord.VoiceState) error { 374 return nil 375 } 376 func (noop) VoiceStateRemove(discord.GuildID, discord.UserID) error { 377 return nil 378 }