package sources import ( "errors" "strings" "sync" ) // ScanErrors is used to collect errors encountered while scanning. // It ensures that errors are collected in a thread-safe manner. type ScanErrors struct { mu sync.RWMutex errors []error } // NewScanErrors creates a new thread safe error collector. func NewScanErrors() *ScanErrors { return &ScanErrors{errors: make([]error, 0)} } // Add an error to the collection in a thread-safe manner. func (s *ScanErrors) Add(err error) { if err == nil { return } s.mu.Lock() defer s.mu.Unlock() s.errors = append(s.errors, err) } // Count returns the number of errors collected. func (s *ScanErrors) Count() uint64 { s.mu.RLock() defer s.mu.RUnlock() return uint64(len(s.errors)) } func (s *ScanErrors) String() string { s.mu.RLock() defer s.mu.RUnlock() var sb strings.Builder sb.WriteString("[") for i, err := range s.errors { sb.WriteString(`"` + err.Error() + `"`) if i < len(s.errors)-1 { sb.WriteString(", ") } } sb.WriteString("]") return sb.String() } func (s *ScanErrors) Errors() error { s.mu.RLock() defer s.mu.RUnlock() return errors.Join(s.errors...) } // TargetedScanError is an error with a secret ID attached. Collections of them can be returned by targeted scans that // scan multiple targets in order to associate individual errors with individual scan targets. type TargetedScanError struct { Err error SecretID int64 } var _ error = (*TargetedScanError)(nil) func (t TargetedScanError) Error() string { return t.Err.Error() } func (t TargetedScanError) Unwrap() error { return t.Err }