152 lines
3.6 KiB
Go
152 lines
3.6 KiB
Go
package archiver
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"zipprine/internal/models"
|
|
)
|
|
|
|
// ComparisonResult holds the result of comparing two archives
|
|
type ComparisonResult struct {
|
|
OnlyInFirst []string
|
|
OnlyInSecond []string
|
|
InBoth []string
|
|
Different []DifferentFile
|
|
Summary string
|
|
}
|
|
|
|
// DifferentFile represents a file that exists in both archives but differs
|
|
type DifferentFile struct {
|
|
Name string
|
|
Size1 int64
|
|
Size2 int64
|
|
ModTime1 string
|
|
ModTime2 string
|
|
}
|
|
|
|
// CompareArchives compares two archives and returns differences
|
|
func CompareArchives(path1, path2 string, type1, type2 models.ArchiveType) (*ComparisonResult, error) {
|
|
// Analyze both archives
|
|
info1, err := AnalyzeArchive(path1, type1)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to analyze first archive: %w", err)
|
|
}
|
|
|
|
info2, err := AnalyzeArchive(path2, type2)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to analyze second archive: %w", err)
|
|
}
|
|
|
|
// Create maps for quick lookup
|
|
files1 := make(map[string]models.FileInfo)
|
|
files2 := make(map[string]models.FileInfo)
|
|
|
|
for _, f := range info1.Files {
|
|
files1[f.Name] = f
|
|
}
|
|
|
|
for _, f := range info2.Files {
|
|
files2[f.Name] = f
|
|
}
|
|
|
|
result := &ComparisonResult{
|
|
OnlyInFirst: []string{},
|
|
OnlyInSecond: []string{},
|
|
InBoth: []string{},
|
|
Different: []DifferentFile{},
|
|
}
|
|
|
|
// Find files only in first archive
|
|
for name := range files1 {
|
|
if _, exists := files2[name]; !exists {
|
|
result.OnlyInFirst = append(result.OnlyInFirst, name)
|
|
}
|
|
}
|
|
|
|
// Find files only in second archive and files in both
|
|
for name, file2 := range files2 {
|
|
if file1, exists := files1[name]; !exists {
|
|
result.OnlyInSecond = append(result.OnlyInSecond, name)
|
|
} else {
|
|
result.InBoth = append(result.InBoth, name)
|
|
|
|
// Check if files are different
|
|
if file1.Size != file2.Size || file1.ModTime != file2.ModTime {
|
|
result.Different = append(result.Different, DifferentFile{
|
|
Name: name,
|
|
Size1: file1.Size,
|
|
Size2: file2.Size,
|
|
ModTime1: file1.ModTime,
|
|
ModTime2: file2.ModTime,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort results for consistent output
|
|
sort.Strings(result.OnlyInFirst)
|
|
sort.Strings(result.OnlyInSecond)
|
|
sort.Strings(result.InBoth)
|
|
|
|
// Generate summary
|
|
result.Summary = fmt.Sprintf(
|
|
"Comparison Summary:\n"+
|
|
" Files in both: %d\n"+
|
|
" Only in first: %d\n"+
|
|
" Only in second: %d\n"+
|
|
" Different: %d",
|
|
len(result.InBoth),
|
|
len(result.OnlyInFirst),
|
|
len(result.OnlyInSecond),
|
|
len(result.Different),
|
|
)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// AnalyzeArchive analyzes an archive and returns information about it
|
|
func AnalyzeArchive(path string, archiveType models.ArchiveType) (*models.ArchiveInfo, error) {
|
|
switch archiveType {
|
|
case models.ZIP:
|
|
return analyzeZip(path)
|
|
case models.TARGZ:
|
|
return analyzeTar(path, true)
|
|
case models.TAR:
|
|
return analyzeTar(path, false)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported archive type: %s", archiveType)
|
|
}
|
|
}
|
|
|
|
// GetArchiveStats returns quick statistics about an archive
|
|
func GetArchiveStats(path string, archiveType models.ArchiveType) (string, error) {
|
|
info, err := AnalyzeArchive(path, archiveType)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
compressionRatio := 0.0
|
|
if info.TotalSize > 0 {
|
|
compressionRatio = (1.0 - float64(info.CompressedSize)/float64(info.TotalSize)) * 100
|
|
}
|
|
|
|
stats := fmt.Sprintf(
|
|
"Archive Statistics:\n"+
|
|
" Type: %s\n"+
|
|
" Files: %d\n"+
|
|
" Total Size: %d bytes\n"+
|
|
" Compressed Size: %d bytes\n"+
|
|
" Compression Ratio: %.2f%%\n"+
|
|
" Checksum: %s",
|
|
info.Type,
|
|
info.FileCount,
|
|
info.TotalSize,
|
|
info.CompressedSize,
|
|
compressionRatio,
|
|
info.Checksum,
|
|
)
|
|
|
|
return stats, nil
|
|
}
|