- Add directory information with Unicode table borders - Add network information with professional formatting - Add version command - Add comprehensive documentation (README.md, DEVELOPMENT.md) - Improve table output with proper borders and alignment - Add project structure (cmd/, pkg/ directories)
7.5 KiB
zutils Development Guide
Quick Start
-
Build the project:
go build -o zutils -
Test locally:
./zutils info main.go -
Install to system:
./install.sh
Project Architecture
Directory Structure
zutils/
├── cmd/ # Command implementations
│ ├── info.go # Main info command router
│ ├── file.go # File info handler
│ ├── dir.go # Directory info handler
│ ├── network.go # Network info handler
│ └── autodetect.go # Auto-detection logic
├── pkg/ # Shared packages
│ ├── colors/ # ANSI color constants
│ ├── formatter/ # Output formatting utilities
│ └── types/ # Command registry and types
├── examples/ # Example files for testing
├── main.go # Application entry point
└── go.mod # Go module definition
How It Works
-
Entry Point (
main.go):- Creates a command registry
- Registers all available commands
- Parses command-line arguments
- Routes to appropriate command handler
-
Command Registry (
pkg/types/registry.go):- Manages all available commands
- Provides registration and lookup functionality
- Makes it easy to add new commands
-
Command Handlers (
cmd/):- Each command has its own file
- Implements the handler function
- Uses shared utilities for output formatting
-
Shared Utilities (
pkg/):colors/: ANSI color codes for terminal outputformatter/: Utility functions for formatting outputtypes/: Command registry and type definitions
Adding a New Command
Step 1: Create the Command File
Create a new file in cmd/ directory:
// cmd/hello.go
package cmd
import (
"fmt"
"github.com/zemenawi/zutils/pkg/colors"
)
func HelloCommand(args []string) error {
fmt.Printf("%s%sHello, World!%s\n", colors.Green, colors.Bold, colors.Reset)
return nil
}
Step 2: Register the Command
Add registration in main.go:
// After existing registrations
registry.Register(&types.Command{
Name: "hello",
Description: "Print a greeting",
Handler: cmd.HelloCommand,
})
Step 3: Test Your Command
go build -o zutils
./zutils hello
Best Practices
1. Use Shared Colors
import "github.com/zemenawi/zutils/pkg/colors"
fmt.Printf("%sSuccess message%s\n", colors.Green, colors.Reset)
fmt.Printf("%sWarning message%s\n", colors.Yellow, colors.Reset)
fmt.Printf("%sError message%s\n", colors.Red, colors.Reset)
2. Use Formatter Utilities
import "github.com/zemenawi/zutils/pkg/formatter"
size := formatter.FormatSize(1234567) // "1.18 MB"
3. Print Box Headers
cmd.PrintBoxHeader("MY HEADER", colors.Cyan)
4. Print Section Headers
cmd.PrintSectionHeader("MY SECTION")
5. Return Errors
Always return errors from command handlers:
func MyCommand(args []string) error {
if len(args) < 1 {
return fmt.Errorf("missing argument")
}
// Your logic here
return nil
}
Color Palette
| Color | Usage | Constant |
|---|---|---|
| Red | Errors | colors.Red |
| Green | Success, positive info | colors.Green |
| Yellow | Warnings, secondary info | colors.Yellow |
| Blue | Primary labels | colors.Blue |
| Purple | Directory info | colors.Purple |
| Cyan | Network info, values | colors.Cyan |
| Gray | Separators | colors.Gray |
Command Pattern
All commands follow this pattern:
package cmd
import (
"fmt"
"github.com/zemenawi/zutils/pkg/colors"
"github.com/zemenawi/zutils/pkg/formatter"
)
func MyCommand(args []string) error {
// Validate arguments
if len(args) < 1 {
return fmt.Errorf("please specify an argument")
}
// Print header
PrintBoxHeader("MY COMMAND", colors.Cyan)
// Your logic here
fmt.Printf("%s%sProcessing: %s%s\n", colors.Blue, colors.Bold, args[0], colors.Reset)
// Print sections
PrintSectionHeader("DETAILS")
fmt.Printf("Size: %s\n", formatter.FormatSize(1234))
fmt.Println()
return nil
}
Auto-Detection
The info command supports auto-detection of files vs directories:
// AutoDetectInfo in cmd/autodetect.go
func AutoDetectInfo(target string) error {
// Checks if path is file or directory
// Calls appropriate handler
}
This allows:
z main.go # Detects as file
z . # Detects as directory
Testing Commands
Unit Tests
Create test files alongside your commands:
// cmd/hello_test.go
package cmd
import "testing"
func TestHelloCommand(t *testing.T) {
err := HelloCommand([]string{})
if err != nil {
t.Errorf("HelloCommand failed: %v", err)
}
}
Integration Tests
Test the full command line:
go build -o zutils
./zutils hello
Common Tasks
Adding a New Color
Edit pkg/colors/colors.go:
const (
// Existing colors...
MyColor = "\033[38;5;123m" // RGB color
)
Adding a New Formatter
Edit pkg/formatter/utils.go:
func MyFormatter(value interface{}) string {
// Your logic
return formattedValue
}
Adding Command Aliases
Register multiple commands with the same handler:
registry.Register(&types.Command{
Name: "info",
Description: "Get information",
Handler: cmd.InfoCommand,
})
registry.Register(&types.Command{
Name: "i",
Description: "Info (alias)",
Handler: cmd.InfoCommand,
})
Debugging
Enable Debug Output
Add a debug flag to your command:
import "flag"
func MyCommand(args []string) error {
debug := flag.Bool("debug", false, "Enable debug output")
flag.Parse()
if *debug {
fmt.Println("Debug mode enabled")
}
// ...
}
Print Structured Data
Use JSON for debugging complex data:
import "encoding/json"
func MyCommand(args []string) error {
data := map[string]interface{}{
"args": args,
"path": "/some/path",
}
jsonBytes, _ := json.MarshalIndent(data, "", " ")
fmt.Println(string(jsonBytes))
return nil
}
Performance Tips
-
Avoid unnecessary file operations:
// Good info, _ := os.Stat(path) size := info.Size() // Bad content, _ := os.ReadFile(path) size := int64(len(content)) -
Use buffered I/O:
scanner := bufio.NewScanner(file) for scanner.Scan() { // Process line } -
Limit directory scanning:
if stats.FileCount > 1000 { return // Stop scanning }
Cross-Platform Considerations
Path Handling
Always use filepath package:
import "path/filepath"
path := filepath.Join("dir", "file.txt")
abs, _ := filepath.Abs(path)
OS-Specific Code
Use build tags or runtime checks:
import "runtime"
if runtime.GOOS == "windows" {
// Windows-specific code
} else if runtime.GOOS == "linux" {
// Linux-specific code
}
Resources
Getting Help
If you're stuck:
- Check existing commands for examples
- Read the command registry implementation
- Look at shared utilities in
pkg/ - Test incrementally with small changes
Happy coding! 🚀