package extractors import ( "fmt" "log/slog" "sort" "sync" ) // extractorStatus tracks the initialization status of an extractor type extractorStatus struct { extractor Extractor available bool errorMsg string } // Manager manages all registered extractors type Manager struct { extractors map[string]*extractorStatus mu sync.RWMutex logger *slog.Logger } // NewManager creates a new extractor manager func NewManager(logger *slog.Logger) *Manager { return &Manager{ extractors: make(map[string]*extractorStatus), logger: logger, } } // Register registers an extractor // If Init() fails, the error is logged but registration continues func (m *Manager) Register(extractor Extractor) error { m.mu.Lock() defer m.mu.Unlock() key := extractor.Key() if _, exists := m.extractors[key]; exists { return fmt.Errorf("extractor with key %s already registered", key) } // Initialize the extractor err := extractor.Init() available := err == nil errorMsg := "" if err != nil { m.logger.Error("Failed to initialize extractor", "key", key, "name", extractor.Name(), "error", err) errorMsg = err.Error() // Continue registration even if Init() fails - extractor will be marked as unavailable } m.extractors[key] = &extractorStatus{ extractor: extractor, available: available, errorMsg: errorMsg, } return nil } // GetExtractorForMimeType retrieves an extractor that supports the given MIME type func (m *Manager) GetExtractorForMimeType(mimeType string) (Extractor, bool) { m.mu.RLock() defer m.mu.RUnlock() for _, status := range m.extractors { if !status.available { continue // Skip unavailable extractors } for _, supportedType := range status.extractor.SupportedMimeTypes() { if supportedType == mimeType { return status.extractor, true } } } return nil, false } // List returns all registered extractors func (m *Manager) List() []Extractor { m.mu.RLock() defer m.mu.RUnlock() var all []Extractor for _, status := range m.extractors { all = append(all, status.extractor) } return all } // ExtractorStatus represents the status of an extractor type ExtractorStatus struct { Key string `json:"key"` Name string `json:"name"` SupportedMimeTypes []string `json:"supported_mime_types"` Available bool `json:"available"` ErrorMessage string `json:"error_message,omitempty"` } // GetStatus returns status information for all registered extractors func (m *Manager) GetStatus() []ExtractorStatus { m.mu.RLock() defer m.mu.RUnlock() var statuses []ExtractorStatus for _, status := range m.extractors { statuses = append(statuses, ExtractorStatus{ Key: status.extractor.Key(), Name: status.extractor.Name(), SupportedMimeTypes: status.extractor.SupportedMimeTypes(), Available: status.available, ErrorMessage: status.errorMsg, }) } // Sort by name sort.Slice(statuses, func(i, j int) bool { return statuses[i].Name < statuses[j].Name }) return statuses }