- Add Go implementation with modular architecture - Support --name flag for exact filename matching - Support --match flag for directory glob pattern matching - Create tar.gz and zip archives preserving directory structure - Handle errors with appropriate exit codes - Skip files with permission errors gracefully - Add comprehensive test suite with 11 test cases
85 lines
No EOL
1.8 KiB
Go
85 lines
No EOL
1.8 KiB
Go
package archiver
|
|
|
|
import (
|
|
"archive/zip"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/atomaka/collect/collector"
|
|
)
|
|
|
|
// ZipArchiver creates zip archives
|
|
type ZipArchiver struct{}
|
|
|
|
// NewZipArchiver creates a new zip archiver
|
|
func NewZipArchiver() *ZipArchiver {
|
|
return &ZipArchiver{}
|
|
}
|
|
|
|
// Create creates a zip archive with the collected files
|
|
func (a *ZipArchiver) Create(outputPath string, files []collector.FileEntry) error {
|
|
// Create output file
|
|
outFile, err := os.Create(outputPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create output file: %w", err)
|
|
}
|
|
defer outFile.Close()
|
|
|
|
// Create zip writer
|
|
zipWriter := zip.NewWriter(outFile)
|
|
defer zipWriter.Close()
|
|
|
|
// Add each file to the archive
|
|
for _, file := range files {
|
|
if err := a.addFileToZip(zipWriter, file); err != nil {
|
|
return fmt.Errorf("failed to add file %s: %w", file.Path, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// addFileToZip adds a single file to the zip archive
|
|
func (a *ZipArchiver) addFileToZip(zw *zip.Writer, file collector.FileEntry) error {
|
|
// Open the file
|
|
f, err := os.Open(file.FullPath)
|
|
if err != nil {
|
|
// Skip files we can't read with a warning
|
|
if os.IsPermission(err) {
|
|
fmt.Fprintf(os.Stderr, "Warning: Cannot read file: %s\n", file.FullPath)
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
// Get file info
|
|
info, err := f.Stat()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create zip file header
|
|
header, err := zip.FileInfoHeader(info)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Use the relative path in the archive
|
|
header.Name = filepath.ToSlash(file.Path)
|
|
|
|
// Set compression method
|
|
header.Method = zip.Deflate
|
|
|
|
// Create writer for this file
|
|
writer, err := zw.CreateHeader(header)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Copy file contents
|
|
_, err = io.Copy(writer, f)
|
|
return err
|
|
} |