package watcher import ( "context" "fmt" "os" "time" ) // FileWatcher monitors a file for changes based on modification time. type FileWatcher struct { filePath string pollInterval time.Duration lastModTime time.Time } // NewFileWatcher creates a new file watcher. func NewFileWatcher(filePath string, pollInterval time.Duration) *FileWatcher { return &FileWatcher{ filePath: filePath, pollInterval: pollInterval, } } // Watch starts watching the file and returns a channel that signals changes. // If the file doesn't exist yet, it will wait until it appears. func (fw *FileWatcher) Watch(ctx context.Context) (<-chan struct{}, error) { changeChan := make(chan struct{}, 1) go func() { defer close(changeChan) ticker := time.NewTicker(fw.pollInterval) defer ticker.Stop() fileExists := false // Wait for file to exist for !fileExists { select { case <-ctx.Done(): return case <-ticker.C: info, err := os.Stat(fw.filePath) if err != nil { // File doesn't exist yet, keep waiting fmt.Printf("Waiting for file to exist: %s\n", fw.filePath) continue } // File now exists! fileExists = true fw.lastModTime = info.ModTime() // Send initial change to process file changeChan <- struct{}{} fmt.Printf("File detected, starting watch: %s\n", fw.filePath) } } // Now watch for changes for { select { case <-ctx.Done(): return case <-ticker.C: info, err := os.Stat(fw.filePath) if err != nil { // File might have been deleted or is temporarily unavailable continue } modTime := info.ModTime() if modTime.After(fw.lastModTime) { fw.lastModTime = modTime changeChan <- struct{}{} } } } }() return changeChan, nil } // GetLastModTime returns the last known modification time of the file. func (fw *FileWatcher) GetLastModTime() time.Time { return fw.lastModTime }