package discord import ( "context" "database/sql" "strings" "sync" "time" "plumber/internal/store" ) // memoryLinks is an in-process DiscordLinkStore for tests. type memoryLinks struct { mu sync.Mutex byPost map[string]store.DiscordLink byMessage map[string]string byThread map[string]string } func newMemoryLinks() *memoryLinks { return &memoryLinks{ byPost: map[string]store.DiscordLink{}, byMessage: map[string]string{}, byThread: map[string]string{}, } } func (m *memoryLinks) GetByPostID(_ context.Context, postID string) (*store.DiscordLink, error) { m.mu.Lock() defer m.mu.Unlock() link, ok := m.byPost[strings.TrimSpace(postID)] if !ok { return nil, sql.ErrNoRows } cp := link return &cp, nil } func (m *memoryLinks) GetByMessageID(_ context.Context, messageID string) (*store.DiscordLink, error) { m.mu.Lock() defer m.mu.Unlock() postID, ok := m.byMessage[strings.TrimSpace(messageID)] if !ok { return nil, sql.ErrNoRows } link := m.byPost[postID] cp := link return &cp, nil } func (m *memoryLinks) GetRootByThreadID(_ context.Context, threadID string) (*store.DiscordLink, error) { m.mu.Lock() defer m.mu.Unlock() postID, ok := m.byThread[strings.TrimSpace(threadID)] if !ok { return nil, sql.ErrNoRows } link := m.byPost[postID] cp := link return &cp, nil } func (m *memoryLinks) Upsert(_ context.Context, link store.DiscordLink) error { m.mu.Lock() defer m.mu.Unlock() link.PostID = strings.TrimSpace(link.PostID) link.MessageID = strings.TrimSpace(link.MessageID) link.ThreadID = strings.TrimSpace(link.ThreadID) if link.CreatedAt == "" { link.CreatedAt = time.Now().UTC().Format(time.RFC3339Nano) } if prev, ok := m.byPost[link.PostID]; ok { delete(m.byMessage, prev.MessageID) if prev.ThreadID != "" { delete(m.byThread, prev.ThreadID) } if link.ThreadID == "" { link.ThreadID = prev.ThreadID } } m.byPost[link.PostID] = link m.byMessage[link.MessageID] = link.PostID if link.ThreadID != "" { m.byThread[link.ThreadID] = link.PostID } return nil }