// Copyright (c) 2021-2024, Ludvig Lundgren and the autobrr contributors. // SPDX-License-Identifier: GPL-2.0-or-later package domain import ( "encoding/json" "fmt" "strings" "sync" ) type Rejection struct { key string got any want any format string } type RejectionReasons struct { m sync.RWMutex data []Rejection } func (r *RejectionReasons) Len() int { return len(r.data) } func NewRejectionReasons() *RejectionReasons { return &RejectionReasons{ data: make([]Rejection, 0), } } func (r *RejectionReasons) String() string { r.m.RLock() defer r.m.RUnlock() if len(r.data) == 0 { return "" } builder := strings.Builder{} for i, rejection := range r.data { if i > 0 { builder.WriteString(", ") } if rejection.format != "" { fmt.Fprintf(&builder, rejection.format, rejection.key, rejection.got, rejection.want) continue } fmt.Fprintf(&builder, "[%s] not matching: got %v want: %v", rejection.key, rejection.got, rejection.want) } return builder.String() } func (r *RejectionReasons) StringTruncated() string { r.m.RLock() defer r.m.RUnlock() if len(r.data) == 0 { return "" } builder := strings.Builder{} for i, rejection := range r.data { got := rejection.got switch v := rejection.got.(type) { case string: if len(v) > 1024 { got = v[:1024] } } want := rejection.want switch v := rejection.want.(type) { case string: if len(v) > 1024 { want = v[:1024] } } if i > 0 { builder.WriteString(", ") } fmt.Fprintf(&builder, "[%s] not matching: got %v want: %v", rejection.key, got, want) } return builder.String() } func (r *RejectionReasons) WriteString() string { r.m.RLock() defer r.m.RUnlock() var output []string for _, rejection := range r.data { output = append(output, fmt.Sprintf("[%s] not matching: got %v want: %v", rejection.key, rejection.got, rejection.want)) } return strings.Join(output, ", ") } func (r *RejectionReasons) WriteJSON() ([]byte, error) { r.m.RLock() defer r.m.RUnlock() var output map[string]string for _, rejection := range r.data { output[rejection.key] = fmt.Sprintf("[%s] not matching: got %v want: %v", rejection.key, rejection.got, rejection.want) } return json.Marshal(output) } func (r *RejectionReasons) Add(key string, got any, want any) { r.m.Lock() defer r.m.Unlock() r.data = append(r.data, Rejection{ key: key, got: got, want: want, }) } func (r *RejectionReasons) Addf(key string, format string, got any, want any) { r.m.Lock() defer r.m.Unlock() r.data = append(r.data, Rejection{ key: key, format: format, got: got, want: want, }) } func (r *RejectionReasons) AddTruncated(key string, got any, want any) { r.m.Lock() defer r.m.Unlock() switch wanted := want.(type) { case string: if len(wanted) > 1024 { want = wanted[:1024] } case []string: for i, s := range wanted { if len(s) > 1024 { wanted[i] = s[:1024] } } } r.Add(key, got, want) } // Clear rejections func (r *RejectionReasons) Clear() { r.m.Lock() defer r.m.Unlock() r.data = make([]Rejection, 0) }