Apply standard Go formatting and update project documentation
- Run go fmt on all Go files to ensure consistent formatting - Add official Go tooling commands to CLAUDE.md for code quality - Update project status to reflect current implementation state
This commit is contained in:
parent
1265f9fb07
commit
bde7aeed90
7 changed files with 104 additions and 72 deletions
32
CLAUDE.md
Normal file
32
CLAUDE.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is a CLI tool called "collect" that recursively collects files matching specific criteria, maintains their file structure, and archives them for backup purposes.
|
||||||
|
|
||||||
|
## Project Status
|
||||||
|
|
||||||
|
The collect CLI tool has been implemented with the following features:
|
||||||
|
- Multiple `--name` flags for exact filename matching
|
||||||
|
- Multiple `--match` flags for directory pattern matching
|
||||||
|
- Support for combining `--name` and `--match` flags with OR logic
|
||||||
|
- Preserving original directory structure in the output
|
||||||
|
- Creating archives in tar.gz or zip format
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
The tool accepts:
|
||||||
|
1. Multiple `--name` and `--match` flags in any combination
|
||||||
|
2. Two positional arguments: source directory and output archive path
|
||||||
|
3. Archive format determined by output file extension (.tgz/.tar.gz or .zip)
|
||||||
|
4. Recursive directory traversal while preserving original structure
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
Before committing changes, run these official Go tools:
|
||||||
|
- `go fmt ./...` - Format all Go files to standard style
|
||||||
|
- `go vet ./...` - Check for common mistakes and suspicious code
|
||||||
|
- `go test ./...` - Run unit tests (when available)
|
||||||
|
- `./test.sh` - Run integration tests
|
|
@ -5,4 +5,4 @@ import "github.com/atomaka/collect/collector"
|
||||||
// Archiver defines the interface for creating archives
|
// Archiver defines the interface for creating archives
|
||||||
type Archiver interface {
|
type Archiver interface {
|
||||||
Create(outputPath string, files []collector.FileEntry) error
|
Create(outputPath string, files []collector.FileEntry) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/atomaka/collect/collector"
|
"github.com/atomaka/collect/collector"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,22 +27,22 @@ func (a *TarArchiver) Create(outputPath string, files []collector.FileEntry) err
|
||||||
return fmt.Errorf("failed to create output file: %w", err)
|
return fmt.Errorf("failed to create output file: %w", err)
|
||||||
}
|
}
|
||||||
defer outFile.Close()
|
defer outFile.Close()
|
||||||
|
|
||||||
// Create gzip writer
|
// Create gzip writer
|
||||||
gzipWriter := gzip.NewWriter(outFile)
|
gzipWriter := gzip.NewWriter(outFile)
|
||||||
defer gzipWriter.Close()
|
defer gzipWriter.Close()
|
||||||
|
|
||||||
// Create tar writer
|
// Create tar writer
|
||||||
tarWriter := tar.NewWriter(gzipWriter)
|
tarWriter := tar.NewWriter(gzipWriter)
|
||||||
defer tarWriter.Close()
|
defer tarWriter.Close()
|
||||||
|
|
||||||
// Add each file to the archive
|
// Add each file to the archive
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if err := a.addFileToTar(tarWriter, file); err != nil {
|
if err := a.addFileToTar(tarWriter, file); err != nil {
|
||||||
return fmt.Errorf("failed to add file %s: %w", file.Path, err)
|
return fmt.Errorf("failed to add file %s: %w", file.Path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,28 +59,28 @@ func (a *TarArchiver) addFileToTar(tw *tar.Writer, file collector.FileEntry) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
// Get file info
|
// Get file info
|
||||||
info, err := f.Stat()
|
info, err := f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create tar header
|
// Create tar header
|
||||||
header, err := tar.FileInfoHeader(info, "")
|
header, err := tar.FileInfoHeader(info, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the relative path in the archive
|
// Use the relative path in the archive
|
||||||
header.Name = filepath.ToSlash(file.Path)
|
header.Name = filepath.ToSlash(file.Path)
|
||||||
|
|
||||||
// Write header
|
// Write header
|
||||||
if err := tw.WriteHeader(header); err != nil {
|
if err := tw.WriteHeader(header); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy file contents
|
// Copy file contents
|
||||||
_, err = io.Copy(tw, f)
|
_, err = io.Copy(tw, f)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/atomaka/collect/collector"
|
"github.com/atomaka/collect/collector"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,18 +26,18 @@ func (a *ZipArchiver) Create(outputPath string, files []collector.FileEntry) err
|
||||||
return fmt.Errorf("failed to create output file: %w", err)
|
return fmt.Errorf("failed to create output file: %w", err)
|
||||||
}
|
}
|
||||||
defer outFile.Close()
|
defer outFile.Close()
|
||||||
|
|
||||||
// Create zip writer
|
// Create zip writer
|
||||||
zipWriter := zip.NewWriter(outFile)
|
zipWriter := zip.NewWriter(outFile)
|
||||||
defer zipWriter.Close()
|
defer zipWriter.Close()
|
||||||
|
|
||||||
// Add each file to the archive
|
// Add each file to the archive
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if err := a.addFileToZip(zipWriter, file); err != nil {
|
if err := a.addFileToZip(zipWriter, file); err != nil {
|
||||||
return fmt.Errorf("failed to add file %s: %w", file.Path, err)
|
return fmt.Errorf("failed to add file %s: %w", file.Path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,32 +54,32 @@ func (a *ZipArchiver) addFileToZip(zw *zip.Writer, file collector.FileEntry) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
// Get file info
|
// Get file info
|
||||||
info, err := f.Stat()
|
info, err := f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create zip file header
|
// Create zip file header
|
||||||
header, err := zip.FileInfoHeader(info)
|
header, err := zip.FileInfoHeader(info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the relative path in the archive
|
// Use the relative path in the archive
|
||||||
header.Name = filepath.ToSlash(file.Path)
|
header.Name = filepath.ToSlash(file.Path)
|
||||||
|
|
||||||
// Set compression method
|
// Set compression method
|
||||||
header.Method = zip.Deflate
|
header.Method = zip.Deflate
|
||||||
|
|
||||||
// Create writer for this file
|
// Create writer for this file
|
||||||
writer, err := zw.CreateHeader(header)
|
writer, err := zw.CreateHeader(header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy file contents
|
// Copy file contents
|
||||||
_, err = io.Copy(writer, f)
|
_, err = io.Copy(writer, f)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (c *Collector) Collect(sourceDir string) ([]FileEntry, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get absolute path: %w", err)
|
return nil, fmt.Errorf("failed to get absolute path: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if source directory exists
|
// Check if source directory exists
|
||||||
info, err := os.Stat(absSourceDir)
|
info, err := os.Stat(absSourceDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -42,9 +42,9 @@ func (c *Collector) Collect(sourceDir string) ([]FileEntry, error) {
|
||||||
if !info.IsDir() {
|
if !info.IsDir() {
|
||||||
return nil, fmt.Errorf("source path is not a directory: %s", sourceDir)
|
return nil, fmt.Errorf("source path is not a directory: %s", sourceDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
var files []FileEntry
|
var files []FileEntry
|
||||||
|
|
||||||
err = filepath.WalkDir(absSourceDir, func(path string, d fs.DirEntry, err error) error {
|
err = filepath.WalkDir(absSourceDir, func(path string, d fs.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Log permission errors but continue walking
|
// Log permission errors but continue walking
|
||||||
|
@ -54,7 +54,7 @@ func (c *Collector) Collect(sourceDir string) ([]FileEntry, error) {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get file info
|
// Get file info
|
||||||
info, err := d.Info()
|
info, err := d.Info()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -64,12 +64,12 @@ func (c *Collector) Collect(sourceDir string) ([]FileEntry, error) {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip symlinks
|
// Skip symlinks
|
||||||
if info.Mode()&os.ModeSymlink != 0 {
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this file should be included
|
// Check if this file should be included
|
||||||
if c.matcher.ShouldInclude(path, info) {
|
if c.matcher.ShouldInclude(path, info) {
|
||||||
// Calculate relative path from source directory
|
// Calculate relative path from source directory
|
||||||
|
@ -77,42 +77,42 @@ func (c *Collector) Collect(sourceDir string) ([]FileEntry, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get relative path: %w", err)
|
return fmt.Errorf("failed to get relative path: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean the relative path to ensure consistent formatting
|
// Clean the relative path to ensure consistent formatting
|
||||||
relPath = filepath.ToSlash(relPath)
|
relPath = filepath.ToSlash(relPath)
|
||||||
|
|
||||||
files = append(files, FileEntry{
|
files = append(files, FileEntry{
|
||||||
Path: relPath,
|
Path: relPath,
|
||||||
FullPath: path,
|
FullPath: path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("walk error: %w", err)
|
return nil, fmt.Errorf("walk error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any files were found
|
// Check if any files were found
|
||||||
if len(files) == 0 {
|
if len(files) == 0 {
|
||||||
return nil, fmt.Errorf("no files found matching criteria")
|
return nil, fmt.Errorf("no files found matching criteria")
|
||||||
}
|
}
|
||||||
|
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetArchiveFormat determines the archive format from the filename
|
// GetArchiveFormat determines the archive format from the filename
|
||||||
func GetArchiveFormat(filename string) string {
|
func GetArchiveFormat(filename string) string {
|
||||||
lower := strings.ToLower(filename)
|
lower := strings.ToLower(filename)
|
||||||
|
|
||||||
if strings.HasSuffix(lower, ".tar.gz") || strings.HasSuffix(lower, ".tgz") {
|
if strings.HasSuffix(lower, ".tar.gz") || strings.HasSuffix(lower, ".tgz") {
|
||||||
return "tar.gz"
|
return "tar.gz"
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(lower, ".zip") {
|
if strings.HasSuffix(lower, ".zip") {
|
||||||
return "zip"
|
return "zip"
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ type PatternMatcher struct {
|
||||||
func NewPatternMatcher(pattern string) *PatternMatcher {
|
func NewPatternMatcher(pattern string) *PatternMatcher {
|
||||||
// Remove trailing slash if present
|
// Remove trailing slash if present
|
||||||
pattern = strings.TrimSuffix(pattern, "/")
|
pattern = strings.TrimSuffix(pattern, "/")
|
||||||
|
|
||||||
return &PatternMatcher{
|
return &PatternMatcher{
|
||||||
pattern: pattern,
|
pattern: pattern,
|
||||||
matchedDirs: make(map[string]bool),
|
matchedDirs: make(map[string]bool),
|
||||||
|
@ -58,27 +58,27 @@ func (m *PatternMatcher) ShouldInclude(path string, info os.FileInfo) bool {
|
||||||
}
|
}
|
||||||
return false // Don't include the directory itself, only files within
|
return false // Don't include the directory itself, only files within
|
||||||
}
|
}
|
||||||
|
|
||||||
// For files, check if any parent directory is in the matched set
|
// For files, check if any parent directory is in the matched set
|
||||||
dir := filepath.Dir(path)
|
dir := filepath.Dir(path)
|
||||||
for {
|
for {
|
||||||
if m.matchedDirs[dir] {
|
if m.matchedDirs[dir] {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also check if this directory matches the pattern (in case we haven't seen it yet)
|
// Also check if this directory matches the pattern (in case we haven't seen it yet)
|
||||||
if matched, err := m.dirMatchesPattern(dir); err == nil && matched {
|
if matched, err := m.dirMatchesPattern(dir); err == nil && matched {
|
||||||
m.matchedDirs[dir] = true
|
m.matchedDirs[dir] = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
parent := filepath.Dir(dir)
|
parent := filepath.Dir(dir)
|
||||||
if parent == dir || parent == "." {
|
if parent == dir || parent == "." {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
dir = parent
|
dir = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,26 +86,26 @@ func (m *PatternMatcher) ShouldInclude(path string, info os.FileInfo) bool {
|
||||||
func (m *PatternMatcher) dirMatchesPattern(dirPath string) (bool, error) {
|
func (m *PatternMatcher) dirMatchesPattern(dirPath string) (bool, error) {
|
||||||
// Get the directory name
|
// Get the directory name
|
||||||
dirName := filepath.Base(dirPath)
|
dirName := filepath.Base(dirPath)
|
||||||
|
|
||||||
// For simple patterns (no path separators), just match the directory name
|
// For simple patterns (no path separators), just match the directory name
|
||||||
if len(m.patternSegments) == 1 {
|
if len(m.patternSegments) == 1 {
|
||||||
return filepath.Match(m.pattern, dirName)
|
return filepath.Match(m.pattern, dirName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For complex patterns, we need to match the full path segments
|
// For complex patterns, we need to match the full path segments
|
||||||
pathSegments := strings.Split(dirPath, string(os.PathSeparator))
|
pathSegments := strings.Split(dirPath, string(os.PathSeparator))
|
||||||
|
|
||||||
// Try to match the pattern segments against the path segments
|
// Try to match the pattern segments against the path segments
|
||||||
if len(pathSegments) < len(m.patternSegments) {
|
if len(pathSegments) < len(m.patternSegments) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check each pattern segment against the corresponding path segment
|
// Check each pattern segment against the corresponding path segment
|
||||||
for i := 0; i < len(m.patternSegments); i++ {
|
for i := 0; i < len(m.patternSegments); i++ {
|
||||||
// Start from the end of both slices
|
// Start from the end of both slices
|
||||||
patternIdx := len(m.patternSegments) - 1 - i
|
patternIdx := len(m.patternSegments) - 1 - i
|
||||||
pathIdx := len(pathSegments) - 1 - i
|
pathIdx := len(pathSegments) - 1 - i
|
||||||
|
|
||||||
matched, err := filepath.Match(m.patternSegments[patternIdx], pathSegments[pathIdx])
|
matched, err := filepath.Match(m.patternSegments[patternIdx], pathSegments[pathIdx])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -114,7 +114,7 @@ func (m *PatternMatcher) dirMatchesPattern(dirPath string) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,4 +136,4 @@ func (m *CompositeMatcher) ShouldInclude(path string, info os.FileInfo) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
42
main.go
42
main.go
|
@ -6,16 +6,16 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/atomaka/collect/archiver"
|
"github.com/atomaka/collect/archiver"
|
||||||
"github.com/atomaka/collect/collector"
|
"github.com/atomaka/collect/collector"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
exitSuccess = 0
|
exitSuccess = 0
|
||||||
exitNoFiles = 1
|
exitNoFiles = 1
|
||||||
exitArchiveError = 2
|
exitArchiveError = 2
|
||||||
exitInvalidArgs = 3
|
exitInvalidArgs = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// stringSlice is a custom flag type that accumulates string values
|
// stringSlice is a custom flag type that accumulates string values
|
||||||
|
@ -34,10 +34,10 @@ func main() {
|
||||||
// Define flags using custom type for multiple values
|
// Define flags using custom type for multiple values
|
||||||
var nameFlags stringSlice
|
var nameFlags stringSlice
|
||||||
var matchFlags stringSlice
|
var matchFlags stringSlice
|
||||||
|
|
||||||
flag.Var(&nameFlags, "name", "Match exact filename (can be specified multiple times)")
|
flag.Var(&nameFlags, "name", "Match exact filename (can be specified multiple times)")
|
||||||
flag.Var(&matchFlags, "match", "Match directory pattern (can be specified multiple times)")
|
flag.Var(&matchFlags, "match", "Match directory pattern (can be specified multiple times)")
|
||||||
|
|
||||||
// Custom usage message
|
// Custom usage message
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprintf(os.Stderr, "Usage: %s [--name <filename>]... [--match <pattern>]... <source-dir> <output-archive>\n\n", os.Args[0])
|
fmt.Fprintf(os.Stderr, "Usage: %s [--name <filename>]... [--match <pattern>]... <source-dir> <output-archive>\n\n", os.Args[0])
|
||||||
|
@ -49,16 +49,16 @@ func main() {
|
||||||
fmt.Fprintf(os.Stderr, " %s --match 'aet-*/' ./ backup.zip\n", os.Args[0])
|
fmt.Fprintf(os.Stderr, " %s --match 'aet-*/' ./ backup.zip\n", os.Args[0])
|
||||||
fmt.Fprintf(os.Stderr, " %s --name .mise.toml --name README.md --match 'test-*' ./ backup.tgz\n", os.Args[0])
|
fmt.Fprintf(os.Stderr, " %s --name .mise.toml --name README.md --match 'test-*' ./ backup.tgz\n", os.Args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// Validate flags
|
// Validate flags
|
||||||
if len(nameFlags) == 0 && len(matchFlags) == 0 {
|
if len(nameFlags) == 0 && len(matchFlags) == 0 {
|
||||||
fmt.Fprintf(os.Stderr, "Error: At least one --name or --match must be specified\n\n")
|
fmt.Fprintf(os.Stderr, "Error: At least one --name or --match must be specified\n\n")
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(exitInvalidArgs)
|
os.Exit(exitInvalidArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check positional arguments
|
// Check positional arguments
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
|
@ -66,30 +66,30 @@ func main() {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(exitInvalidArgs)
|
os.Exit(exitInvalidArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceDir := args[0]
|
sourceDir := args[0]
|
||||||
outputPath := args[1]
|
outputPath := args[1]
|
||||||
|
|
||||||
// Determine archive format
|
// Determine archive format
|
||||||
format := collector.GetArchiveFormat(outputPath)
|
format := collector.GetArchiveFormat(outputPath)
|
||||||
if format == "" {
|
if format == "" {
|
||||||
fmt.Fprintf(os.Stderr, "Error: Unsupported archive format. Use .tar.gz, .tgz, or .zip\n")
|
fmt.Fprintf(os.Stderr, "Error: Unsupported archive format. Use .tar.gz, .tgz, or .zip\n")
|
||||||
os.Exit(exitInvalidArgs)
|
os.Exit(exitInvalidArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create matchers
|
// Create matchers
|
||||||
var matchers []collector.Matcher
|
var matchers []collector.Matcher
|
||||||
|
|
||||||
// Add name matchers
|
// Add name matchers
|
||||||
for _, name := range nameFlags {
|
for _, name := range nameFlags {
|
||||||
matchers = append(matchers, collector.NewNameMatcher(name))
|
matchers = append(matchers, collector.NewNameMatcher(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add pattern matchers
|
// Add pattern matchers
|
||||||
for _, pattern := range matchFlags {
|
for _, pattern := range matchFlags {
|
||||||
matchers = append(matchers, collector.NewPatternMatcher(pattern))
|
matchers = append(matchers, collector.NewPatternMatcher(pattern))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a composite matcher if we have multiple matchers, otherwise use the single one
|
// Create a composite matcher if we have multiple matchers, otherwise use the single one
|
||||||
var matcher collector.Matcher
|
var matcher collector.Matcher
|
||||||
if len(matchers) == 1 {
|
if len(matchers) == 1 {
|
||||||
|
@ -97,7 +97,7 @@ func main() {
|
||||||
} else {
|
} else {
|
||||||
matcher = collector.NewCompositeMatcher(matchers)
|
matcher = collector.NewCompositeMatcher(matchers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create collector and collect files
|
// Create collector and collect files
|
||||||
c := collector.New(matcher)
|
c := collector.New(matcher)
|
||||||
files, err := c.Collect(sourceDir)
|
files, err := c.Collect(sourceDir)
|
||||||
|
@ -108,10 +108,10 @@ func main() {
|
||||||
}
|
}
|
||||||
os.Exit(exitArchiveError)
|
os.Exit(exitArchiveError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report number of files found
|
// Report number of files found
|
||||||
fmt.Printf("Found %d files to archive\n", len(files))
|
fmt.Printf("Found %d files to archive\n", len(files))
|
||||||
|
|
||||||
// Create appropriate archiver
|
// Create appropriate archiver
|
||||||
var arch archiver.Archiver
|
var arch archiver.Archiver
|
||||||
switch format {
|
switch format {
|
||||||
|
@ -120,14 +120,14 @@ func main() {
|
||||||
case "zip":
|
case "zip":
|
||||||
arch = archiver.NewZipArchiver()
|
arch = archiver.NewZipArchiver()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create archive
|
// Create archive
|
||||||
if err := arch.Create(outputPath, files); err != nil {
|
if err := arch.Create(outputPath, files); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error creating archive: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error creating archive: %v\n", err)
|
||||||
os.Exit(exitArchiveError)
|
os.Exit(exitArchiveError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get absolute path for cleaner output
|
// Get absolute path for cleaner output
|
||||||
absOutput, _ := filepath.Abs(outputPath)
|
absOutput, _ := filepath.Abs(outputPath)
|
||||||
fmt.Printf("Archive created successfully: %s\n", absOutput)
|
fmt.Printf("Archive created successfully: %s\n", absOutput)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue