Change FileProgress callback to channel-based progress
Replace callback-based progress reporting in Builder.AddFile with channel-based FileHashProgress for consistency with EnumerateStatus and ScanStatus patterns. Update scanner.go to use the new channel API.
This commit is contained in:
@@ -32,11 +32,12 @@ type EnumerateStatus struct {
|
||||
|
||||
// ScanStatus contains progress information for the scan phase.
|
||||
type ScanStatus struct {
|
||||
TotalFiles int64 // Total number of files to scan
|
||||
ScannedFiles int64 // Number of files scanned so far
|
||||
TotalBytes int64 // Total bytes to read (sum of all file sizes)
|
||||
ScannedBytes int64 // Bytes read so far
|
||||
BytesPerSec float64 // Current throughput rate
|
||||
TotalFiles int64 // Total number of files to scan
|
||||
ScannedFiles int64 // Number of files scanned so far
|
||||
TotalBytes int64 // Total bytes to read (sum of all file sizes)
|
||||
ScannedBytes int64 // Bytes read so far
|
||||
BytesPerSec float64 // Current throughput rate
|
||||
ETA time.Duration // Estimated time to completion
|
||||
}
|
||||
|
||||
// Options configures scanner behavior.
|
||||
@@ -177,6 +178,31 @@ func (s *Scanner) enumerateFileWithInfo(filePath string, basePath string, info f
|
||||
// Compute absolute path for file reading
|
||||
absPath := filepath.Join(basePath, cleanPath)
|
||||
|
||||
// Handle symlinks
|
||||
if info.Mode()&fs.ModeSymlink != 0 {
|
||||
if !s.options.FollowSymLinks {
|
||||
// Skip symlinks when not following them
|
||||
return nil
|
||||
}
|
||||
// Resolve symlink to get real file info
|
||||
realPath, err := filepath.EvalSymlinks(absPath)
|
||||
if err != nil {
|
||||
// Skip broken symlinks
|
||||
return nil
|
||||
}
|
||||
realInfo, err := s.fs.Stat(realPath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// Skip if symlink points to a directory
|
||||
if realInfo.IsDir() {
|
||||
return nil
|
||||
}
|
||||
// Use resolved path for reading, but keep original path in manifest
|
||||
absPath = realPath
|
||||
info = realInfo
|
||||
}
|
||||
|
||||
entry := &FileEntry{
|
||||
Path: cleanPath,
|
||||
AbsPath: absPath,
|
||||
@@ -270,34 +296,58 @@ func (s *Scanner) ToManifest(ctx context.Context, w io.Writer, progress chan<- S
|
||||
return err
|
||||
}
|
||||
|
||||
// Add to manifest with progress callback
|
||||
// Create progress channel for this file
|
||||
var fileProgress chan mfer.FileHashProgress
|
||||
var wg sync.WaitGroup
|
||||
if progress != nil {
|
||||
fileProgress = make(chan mfer.FileHashProgress, 1)
|
||||
wg.Add(1)
|
||||
go func(baseScannedBytes int64) {
|
||||
defer wg.Done()
|
||||
for p := range fileProgress {
|
||||
// Send progress at most once per second
|
||||
now := time.Now()
|
||||
if now.Sub(lastProgressTime) >= time.Second {
|
||||
elapsed := now.Sub(startTime).Seconds()
|
||||
currentBytes := baseScannedBytes + p.BytesRead
|
||||
var rate float64
|
||||
var eta time.Duration
|
||||
if elapsed > 0 && currentBytes > 0 {
|
||||
rate = float64(currentBytes) / elapsed
|
||||
remainingBytes := totalBytes - currentBytes
|
||||
if rate > 0 {
|
||||
eta = time.Duration(float64(remainingBytes)/rate) * time.Second
|
||||
}
|
||||
}
|
||||
sendScanStatus(progress, ScanStatus{
|
||||
TotalFiles: totalFiles,
|
||||
ScannedFiles: scannedFiles,
|
||||
TotalBytes: totalBytes,
|
||||
ScannedBytes: currentBytes,
|
||||
BytesPerSec: rate,
|
||||
ETA: eta,
|
||||
})
|
||||
lastProgressTime = now
|
||||
}
|
||||
}
|
||||
}(scannedBytes)
|
||||
}
|
||||
|
||||
// Add to manifest with progress channel
|
||||
bytesRead, err := builder.AddFile(
|
||||
entry.Path,
|
||||
entry.Size,
|
||||
entry.Mtime,
|
||||
f,
|
||||
func(fileBytes int64) {
|
||||
// Send progress at most once per second
|
||||
now := time.Now()
|
||||
if progress != nil && now.Sub(lastProgressTime) >= time.Second {
|
||||
elapsed := now.Sub(startTime).Seconds()
|
||||
currentBytes := scannedBytes + fileBytes
|
||||
var rate float64
|
||||
if elapsed > 0 {
|
||||
rate = float64(currentBytes) / elapsed
|
||||
}
|
||||
sendScanStatus(progress, ScanStatus{
|
||||
TotalFiles: totalFiles,
|
||||
ScannedFiles: scannedFiles,
|
||||
TotalBytes: totalBytes,
|
||||
ScannedBytes: currentBytes,
|
||||
BytesPerSec: rate,
|
||||
})
|
||||
lastProgressTime = now
|
||||
}
|
||||
},
|
||||
fileProgress,
|
||||
)
|
||||
f.Close()
|
||||
_ = f.Close()
|
||||
|
||||
// Close channel and wait for goroutine to finish
|
||||
if fileProgress != nil {
|
||||
close(fileProgress)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -307,7 +357,7 @@ func (s *Scanner) ToManifest(ctx context.Context, w io.Writer, progress chan<- S
|
||||
scannedBytes += bytesRead
|
||||
}
|
||||
|
||||
// Send final progress
|
||||
// Send final progress (ETA is 0 at completion)
|
||||
if progress != nil {
|
||||
elapsed := time.Since(startTime).Seconds()
|
||||
var rate float64
|
||||
@@ -320,6 +370,7 @@ func (s *Scanner) ToManifest(ctx context.Context, w io.Writer, progress chan<- S
|
||||
TotalBytes: totalBytes,
|
||||
ScannedBytes: scannedBytes,
|
||||
BytesPerSec: rate,
|
||||
ETA: 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user