Skip to content

Commit

Permalink
refactor: Update version information in resources.rc file
Browse files Browse the repository at this point in the history
  • Loading branch information
itsfuad committed Jul 10, 2024
1 parent 2bd7535 commit 26e5bf1
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 139 deletions.
161 changes: 107 additions & 54 deletions compressor/compress.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"fmt"
"os"
"path"
"sync"
"path/filepath"
"strings"
"sync"
"sync/atomic"

"file-compressor/utils"
"file-compressor/encryption"
"file-compressor/utils"
)

// Compress compresses the specified files or folders into a single compressed file.
Expand All @@ -26,6 +28,8 @@ func Compress(filenameStrs []string, outputDir *string, password *string) error

// Prepare to store files' content
var files []utils.File
var originalSize int64
var compressedSize int64

// Use a wait group to synchronize goroutines
var wg sync.WaitGroup
Expand All @@ -34,40 +38,52 @@ func Compress(filenameStrs []string, outputDir *string, password *string) error
// Channel to receive errors from goroutines
errChan := make(chan error, len(filenameStrs))

// Process each input file or folder
// Function to handle compression of a single file or directory
compressFileOrFolder := func(filePath string) error {
// Check if the file or folder exists
info, err := os.Stat(filePath)
if os.IsNotExist(err) {
return fmt.Errorf("file or folder does not exist: %s", filePath)
}

if info.IsDir() {
utils.ColorPrint(utils.YELLOW, fmt.Sprintf("Compressing folder (%s)\n", filePath))
err := compressFolderRecursive(filePath, &files, &originalSize)
if err != nil {
return err
}
} else {
utils.ColorPrint(utils.YELLOW, fmt.Sprintf("Compressing file (%s)\n", filePath))
// Read file content
content, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("failed to read file %s: %w", filePath, err)
}

// Store file information (name and content)
files = append(files, utils.File{
Name: path.Base(filePath),
Content: content,
})

// Increment original size
atomic.AddInt64(&originalSize, info.Size())
}
return nil
}

// Process each input file or folder recursively
for _, filename := range filenameStrs {
wg.Add(1)
go func(filename string) {
file := filename
go func(filePath string) {
defer wg.Done()

// Check if the file or folder exists
info, err := os.Stat(filename)
if os.IsNotExist(err) {
errChan <- fmt.Errorf("file or folder does not exist: %s", filename)
return
}

// Handle directory recursively
if info.IsDir() {
err := compressFolderRecursive(filename, &files)
if err != nil {
errChan <- err
}
} else {
// Read file content
content, err := os.ReadFile(filename)
if err != nil {
errChan <- fmt.Errorf("failed to read file %s: %w", filename, err)
return
}

// Store file information (name and content)
files = append(files, utils.File{
Name: path.Base(filename),
Content: content,
})
err := compressFileOrFolder(filePath)
if err != nil {
errChan <- err
}
}(filename)
}(file)
}

// Wait for all goroutines to finish
Expand All @@ -84,50 +100,58 @@ func Compress(filenameStrs []string, outputDir *string, password *string) error
}

// Compress files using Huffman coding
compressedFile := Zip(files)
compressedFile, err := Zip(files)
if err != nil {
return err
}

// Encrypt compressed content if password is provided
if password != nil && *password != "" {
var err error
compressedFile.Content, err = encryption.Encrypt(compressedFile.Content, *password)
if err != nil {
return fmt.Errorf("encryption error: %w", err)
}
}

//check if the output file and directory exists
// Check if the output directory exists; create if not
if _, err := os.Stat(*outputDir); os.IsNotExist(err) {
err := os.MkdirAll(*outputDir, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}
}

// if the output file already exists, rename it with file(N).bin
if _, err := os.Stat(*outputDir + "/" + compressedFile.Name + ".bin"); err == nil {
count := 1
for {
if _, err := os.Stat(*outputDir + "/" + compressedFile.Name + fmt.Sprintf("(%d).bin", count)); os.IsNotExist(err) {
compressedFile.Name = compressedFile.Name + fmt.Sprintf("(%d).bin", count)
break
}
count++
}
} else if os.IsNotExist(err) {
compressedFile.Name = compressedFile.Name + ".bin"
// Check if the output file already exists; rename if necessary
if _, err := os.Stat(*outputDir + "/" + compressedFile.Name); err == nil {
InvalidateFileName(&compressedFile, outputDir)
}

// Write compressed file to the output directory
err := os.WriteFile(*outputDir+"/"+compressedFile.Name, compressedFile.Content, 0644)
err = os.WriteFile(*outputDir+"/"+compressedFile.Name, compressedFile.Content, 0644)
if err != nil {
return fmt.Errorf("failed to write compressed file: %w", err)
}

//get the file size
compressedFileInfo, err := os.Stat(*outputDir + "/" + compressedFile.Name)
if err != nil {
return fmt.Errorf("failed to get compressed file info: %w", err)
}

compressedSize = compressedFileInfo.Size()

// Calculate compression ratio
compressionRatio := float64(compressedSize) / float64(originalSize)

// Print compression statistics
utils.ColorPrint(utils.GREEN, fmt.Sprintf("Compression complete: Original Size: %s, Compressed Size: %s, Compression Ratio: %.2f%%\n",
utils.FileSize(originalSize), utils.FileSize(compressedSize), compressionRatio*100))

return nil
}

// Function to recursively compress a folder and its contents
func compressFolderRecursive(folderPath string, files *[]utils.File) error {
func compressFolderRecursive(folderPath string, files *[]utils.File, originalSize *int64) error {
// Traverse the folder contents
err := filepath.Walk(folderPath, func(filePath string, info os.FileInfo, err error) error {
if err != nil {
Expand All @@ -140,16 +164,19 @@ func compressFolderRecursive(folderPath string, files *[]utils.File) error {
return fmt.Errorf("failed to read file %s: %w", filePath, err)
}

filename, err := filepath.Rel(folderPath, filePath)
// Store file information (relative path and content)
relativePath, err := filepath.Rel(folderPath, filePath)
if err != nil {
return fmt.Errorf("failed to get relative path for file %s: %w", filePath, err)
}

// Store file information (relative path and content)
*files = append(*files, utils.File{
Name: filepath.ToSlash(filename), // Store relative path
Name: filepath.ToSlash(relativePath), // Store relative path
Content: content,
})

// Increment original size and compressed size
atomic.AddInt64(originalSize, info.Size())
}
return nil
})
Expand All @@ -161,6 +188,7 @@ func compressFolderRecursive(folderPath string, files *[]utils.File) error {
return nil
}


// Decompress decompresses the specified compressed file into individual files or folders.
func Decompress(filenameStrs []string, outputDir *string, password *string) error {
// Check if there are files to decompress
Expand Down Expand Up @@ -188,11 +216,15 @@ func Decompress(filenameStrs []string, outputDir *string, password *string) erro
}

// Decompress file using Huffman coding
files := Unzip(utils.File{
files, err := Unzip(utils.File{
Name: path.Base(filenameStrs[0]),
Content: compressedContent,
})

if err != nil {
return err
}

// Use a wait group to synchronize goroutines
var wg sync.WaitGroup
var errMutex sync.Mutex // Mutex to handle errors safely
Expand All @@ -214,13 +246,18 @@ func Decompress(filenameStrs []string, outputDir *string, password *string) erro
return
}

// Check if the file already exists, rename it with file_N
if _, err := os.Stat(filepath.Join(*outputDir, file.Name)); err == nil {
InvalidateFileName(&file, outputDir)
}

// Write decompressed file content
err = os.WriteFile(filepath.Join(*outputDir, file.Name), file.Content, 0644)
if err != nil {
errChan <- fmt.Errorf("failed to write decompressed file %s: %w", file.Name, err)
return
}
utils.ColorPrint(utils.GREEN, fmt.Sprintf("Decompressed file: %s\n", file.Name))
utils.ColorPrint(utils.YELLOW, fmt.Sprintf("Decompressed file: %s\n", file.Name))
}(file)
}

Expand All @@ -238,4 +275,20 @@ func Decompress(filenameStrs []string, outputDir *string, password *string) erro
}

return nil
}
}

func InvalidateFileName(file *utils.File, outputDir *string) {
fileExt := path.Ext(file.Name)
//extract the file name without the extension
filename := path.Base(file.Name)
filename = strings.TrimSuffix(filename, fileExt)
count := 1
for {
if _, err := os.Stat(filepath.Join(*outputDir, filepath.Dir(file.Name), filename+fmt.Sprintf("_%d%s", count, fileExt))); os.IsNotExist(err) {
utils.ColorPrint(utils.PURPLE, fmt.Sprintf("File %s already exists, renaming to %s\n", file.Name, filename+fmt.Sprintf("_%d%s", count, fileExt)))
file.Name = filepath.Dir(file.Name) + "/" + filename + fmt.Sprintf("_%d%s", count, fileExt)
break
}
count++
}
}
Loading

0 comments on commit 26e5bf1

Please sign in to comment.