Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
.env

# Binaries for programs and plugins
gitfit
/gitfit
*.exe
*.out
*.test
Expand All @@ -21,4 +21,6 @@ gitfit-compressed-*.jpg

# Logs and databases
*.log
coverage
coverage
output.txt
output_*.txt
90 changes: 39 additions & 51 deletions cmd/gitfit/main.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package main

import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/nabiladem/git-fit/internal/compressor"
"github.com/nabiladem/git-fit/internal/gravatar"
"flag"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/nabiladem/git-fit/internal/compressor"
"github.com/nabiladem/git-fit/internal/gravatar"
)

// Config holds parsed command-line options
Expand All @@ -24,6 +24,7 @@ type Config struct {
Verbose bool
UploadGravatar bool
}

// main() - entry point
func main() {
cfg := parseFlags()
Expand All @@ -49,43 +50,44 @@ func main() {

// parseFlags() - extract flags into a Config struct
func parseFlags() *Config {
// define command-line flags
// define command-line flags
inputPath := flag.String("input", "", "Path to the input image file")
outputPath := flag.String("output", "", "Path to save the compressed image")
maxSize := flag.Int("maxsize", 1048576, "Maximum file size in bytes (default 1MB)")
outputFormat := flag.String("format", "", "Output image format (jpeg, png, or gif)")
quality := flag.Int("quality", 85, "JPEG compression quality (1-100; 85 by default)")
verbose := flag.Bool("v", false, "Verbose logging enabled")
uploadGravatar := flag.Bool("upload-gravatar", false, "Upload compressed image to Gravatar")
uploadGravatar := flag.Bool("upload-gravatar", false, "Upload compressed image to Gravatar")

// custom usage message for flags
// custom usage message for flags
flag.Usage = func() {
fmt.Println("Usage: gitfit -input <input-image-file> -output <output-image-file> -maxsize <max size in bytes> " +
"-format <jpeg|png|gif> -quality <0-100> -v [for verbose logging]")
fmt.Println("Example: gitfit -input input.jpeg -output output.jpeg -maxsize 1000000 -format jpeg -quality 85 -v")
"-format <jpeg|png|gif> -quality <0-100> -v [for verbose logging] -upload-gravatar [to upload to Gravatar]")
fmt.Println("Example: gitfit -input input.jpeg -output output.jpeg -maxsize 1000000 -format jpeg -quality 85 -v -upload-gravatar")
fmt.Println("Flags:")
flag.PrintDefaults()
}

flag.Parse()

// Config struct populated with flag values
return &Config {
InputPath: *inputPath,
OutputPath: *outputPath,
MaxSize: *maxSize,
OutputFormat: *outputFormat,
Quality: *quality,
Verbose: *verbose,
// Config struct populated with flag values
return &Config{
InputPath: *inputPath,
OutputPath: *outputPath,
MaxSize: *maxSize,
OutputFormat: *outputFormat,
Quality: *quality,
Verbose: *verbose,
UploadGravatar: *uploadGravatar,
}
}

// validateConfig() - perform validations and sets defaults and returns if usage should be shown
// validateConfig() - perform validations and sets defaults, returns if usage should be shown
/* cfg (*Config) - configuration to validate */
func validateConfig(cfg *Config) (bool, error) {
// check if input and/or output path is missing
if cfg.InputPath == "" || cfg.OutputPath == "" {
// assume user knows about both flags if one is given
// check if input and/or output path is missing
if cfg.InputPath == "" || cfg.OutputPath == "" {
// assume user knows about both flags if one is given
if cfg.InputPath == "" && cfg.OutputPath == "" {
return true, nil
}
Expand All @@ -97,51 +99,37 @@ func validateConfig(cfg *Config) (bool, error) {
return false, fmt.Errorf("input file %s does not exist", cfg.InputPath)
}

// set default output format based on input file extension if not provided
// set default output format based on input file extension if not provided
if cfg.OutputFormat == "" {
extension := strings.ToLower(filepath.Ext(cfg.InputPath))
switch extension {
case ".jpg", ".jpeg":
cfg.OutputFormat = "jpeg"
case ".png":
cfg.OutputFormat = "png"
case ".gif":
cfg.OutputFormat = "gif"
default:
cfg.OutputFormat = "jpeg"
return false, fmt.Errorf("unsupported input file extension: %s. Please specify format explicitly", extension)
}
}

// append appropriate file extension to output path if missing
if filepath.Ext(cfg.OutputPath) == "" {
cfg.OutputPath = cfg.OutputPath + "." + cfg.OutputFormat
if cfg.MaxSize <= 0 {
return false, fmt.Errorf("max size must be greater than 0")
}

if cfg.Quality <= 0 || cfg.Quality > 100 {
return false, fmt.Errorf("value for -quality must be between 1 and 100 inclusive")
}

if cfg.Verbose && cfg.Quality == 85 {
fmt.Println("Using default quality of 85 for JPEG compression.")
}

if cfg.Verbose {
fmt.Printf("Input file: %s\nOutput file: %s\nMaximum size: %d\nOutput format: %s\n",
cfg.InputPath, cfg.OutputPath, cfg.MaxSize, cfg.OutputFormat)

if cfg.OutputFormat == "jpeg" {
fmt.Println("Quality:", cfg.Quality)
}
}

return false, nil
}

// runCompress() - call the compressor with the provided Config
/* cfg (*Config) - configuration for compression */
// runCompress() - call the compressor with the provided Config
/* cfg (*Config) - configuration for compression */
func runCompress(cfg *Config) error {
err := compressor.CompressImage(cfg.InputPath, cfg.OutputPath, cfg.MaxSize,
cfg.OutputFormat, cfg.Quality, cfg.Verbose)
cfg.OutputFormat, cfg.Quality, cfg.Verbose)
if err != nil {
return err
}
Expand All @@ -151,7 +139,7 @@ cfg.OutputFormat, cfg.Quality, cfg.Verbose)
fmt.Println("Uploading to Gravatar...")
}

// Load OAuth credentials from environment
// load OAuth credentials from environment
clientID := os.Getenv("GRAVATAR_CLIENT_ID")
clientSecret := os.Getenv("GRAVATAR_CLIENT_SECRET")
redirectURI := os.Getenv("GRAVATAR_REDIRECT_URI")
Expand All @@ -160,15 +148,15 @@ cfg.OutputFormat, cfg.Quality, cfg.Verbose)
return fmt.Errorf("GRAVATAR_CLIENT_ID and GRAVATAR_CLIENT_SECRET environment variables must be set")
}

// Use default redirect URI if not specified
// use default redirect URI if not specified
if redirectURI == "" {
redirectURI = "http://localhost:8080/callback"
}

// Create OAuth client
// create OAuth client
client := gravatar.NewClient(clientID, clientSecret, redirectURI, cfg.Verbose)

// Perform OAuth authentication
// perform OAuth authentication
if cfg.Verbose {
fmt.Println("Starting OAuth authentication...")
fmt.Println("Your browser will open for authorization.")
Expand All @@ -178,7 +166,7 @@ cfg.OutputFormat, cfg.Quality, cfg.Verbose)
return fmt.Errorf("OAuth authentication failed: %v", err)
}

// Upload avatar
// upload avatar
if err := client.UploadAvatar(cfg.OutputPath); err != nil {
return fmt.Errorf("failed to upload to Gravatar: %v", err)
}
Expand Down
Loading
Loading