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  }