aboutsummaryrefslogtreecommitdiff
path: root/user/state.go
blob: 07096db021ac7786a030fbc27406d619826455b3 (plain)
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
package user

import (
	"context"
	"sync"

	"github.com/nyttikord/gokord/state"
)

type CopaingCached struct {
	ID        uint   `gorm:"primarykey"`
	DiscordID string `gorm:"not null"`
	GuildID   string `gorm:"not null"`
	XPs       uint
	XPToAdd   uint
}

// Copaing turns a CopaingCached into a Copaing.
// This operation is heavy.
func (cc *CopaingCached) Copaing(ctx context.Context) *Copaing {
	c := Copaing{DiscordID: cc.DiscordID, GuildID: cc.GuildID}
	if err := c.Load(ctx); err != nil {
		panic(err)
	}
	return &c
}

func (cc *CopaingCached) Save(ctx context.Context) error {
	state := GetState(ctx)

	state.mu.Lock()
	defer state.mu.Unlock()

	return state.storage.Write(KeyCopaingCachedRaw(cc.GuildID, cc.DiscordID), *cc)
}

func FromCopaing(c *Copaing) *CopaingCached {
	return &CopaingCached{
		ID:        c.ID,
		DiscordID: c.DiscordID,
		GuildID:   c.GuildID,
		XPs:       calcXP(c),
		XPToAdd:   0,
	}
}

const KeyCopaingCachedPrefix = "cc:"

func KeyCopaingCached(c *Copaing) state.Key {
	return KeyCopaingCachedRaw(c.GuildID, c.DiscordID)
}

func KeyCopaingCachedRaw(guildID, copaingID string) state.Key {
	return KeyCopaingCachedPrefix + state.Key(guildID+":"+copaingID)
}

type State struct {
	mu      sync.RWMutex
	storage state.MapStorage[CopaingCached]
}

func NewState() *State {
	return &State{
		storage: state.MapStorage[CopaingCached]{},
	}
}

const ContextKeyState = "state"

func GetState(ctx context.Context) *State {
	return ctx.Value(ContextKeyState).(*State)
}

func SetState(ctx context.Context, state *State) context.Context {
	return context.WithValue(ctx, ContextKeyState, state)
}

func (s *State) Copaing(guildID, copaingID string) (*CopaingCached, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	c, err := s.storage.Get(KeyCopaingCachedRaw(guildID, copaingID))
	if err != nil {
		return nil, err
	}
	mC := c.(CopaingCached)
	return &mC, nil
}

// CopaingAdd does not call Copaing.Load!
func (s *State) CopaingAdd(c *Copaing, xpToAdd uint) (*CopaingCached, error) {
	var err error
	var cc *CopaingCached
	if cc, err = s.Copaing(c.GuildID, c.DiscordID); err == nil {
		cc.XPs = calcXP(c)
		cc.XPToAdd = xpToAdd
	} else {
		cc = FromCopaing(c)
	}
	s.mu.Lock()
	defer s.mu.Unlock()

	return cc, s.storage.Write(KeyCopaingCached(c), *cc)
}

func (s *State) CopaingRemove(c *Copaing) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	return s.storage.Delete(KeyCopaingCached(c))
}

func calcXP(c *Copaing) uint {
	var sum uint
	for _, entry := range c.CopaingXPs {
		sum += entry.XP
	}
	return sum
}