package parser import ( "fmt" "log/slog" "sync" "github.com/wailsapp/wails/v3/pkg/application" ) // Job represents a file parsing task. type Job struct { FilePath string FileID string ProjectID string } // Result is the outcome of a parsing job. type Result struct { Job Job Content string Err error } // ProcessingQueue manages concurrent file parsing with a worker pool. type ProcessingQueue struct { registry *Registry jobs chan Job results chan Result concurrency int wg sync.WaitGroup onComplete func(Result) } // NewProcessingQueue creates a queue with the given concurrency limit. func NewProcessingQueue(registry *Registry, concurrency int, onComplete func(Result)) *ProcessingQueue { q := &ProcessingQueue{ registry: registry, jobs: make(chan Job, 100), results: make(chan Result, 100), concurrency: concurrency, onComplete: onComplete, } q.start() return q } func (q *ProcessingQueue) start() { // Start workers for i := 0; i < q.concurrency; i++ { go q.worker(i) } // Start result collector go func() { for result := range q.results { if q.onComplete != nil { q.onComplete(result) } } }() } func (q *ProcessingQueue) worker(id int) { for job := range q.jobs { slog.Info("parser worker processing", "worker", id, "file", job.FilePath) content, err := q.registry.Parse(job.FilePath) if err != nil { slog.Error("parse failed", "file", job.FilePath, "err", err) } q.results <- Result{Job: job, Content: content, Err: err} q.wg.Done() } } // Submit adds a job to the processing queue. func (q *ProcessingQueue) Submit(job Job) { q.wg.Add(1) q.jobs <- job } // Wait blocks until all queued jobs are complete. func (q *ProcessingQueue) Wait() { q.wg.Wait() } // Close shuts down the queue. func (q *ProcessingQueue) Close() { close(q.jobs) q.wg.Wait() close(q.results) } // ParseService wraps the parsing pipeline for Wails binding. type ParseService struct { registry *Registry queue *ProcessingQueue } // NewParseService creates a new parse service. func NewParseService() *ParseService { return &ParseService{ registry: NewRegistry(), } } // ParseFile synchronously parses a single file. For Wails binding. func (s *ParseService) ParseFile(path string) (string, error) { content, err := s.registry.Parse(path) if err != nil { return "", fmt.Errorf("parse %s: %w", path, err) } return content, nil } // GetSupportedTypes returns supported file extensions. func (s *ParseService) GetSupportedTypes() []string { return []string{".xlsx", ".xls", ".pdf", ".dwg", ".dxf", ".docx"} } // ParseDeliveryStandard opens a file dialog to select a document, parses it, and returns the markdown. func (s *ParseService) ParseDeliveryStandard() (string, error) { dialog := application.Get().Dialog.OpenFile() dialog.SetTitle("选择交付标准文件 (Delivery Standard)") dialog.AddFilter("Documents", "*.pdf;*.xlsx;*.xls;*.docx") path, err := dialog.PromptForSingleSelection() if err != nil { return "", fmt.Errorf("open file dialog: %w", err) } if path == "" { return "", nil // user cancelled } return s.ParseFile(path) }