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
19 changes: 19 additions & 0 deletions compressor/7z.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ import (
// type: 7z
// password: (optional) password for encryption
// compression_level: (optional) 0-9, default 5
// method: (optional) compression method, e.g., LZMA, LZMA2, PPMd, BZip2, Deflate, Copy
// volume_size: (optional) split archive into volumes of specified size (e.g., "100m", "1g")
// args: (optional) additional 7z arguments
type SevenZip struct {
Base
}

// HasVolumeSize returns true if native 7z volume splitting is enabled
func (sz *SevenZip) HasVolumeSize() bool {
return len(sz.viper.GetString("volume_size")) > 0
}

func (sz *SevenZip) perform() (archivePath string, err error) {
filePath := sz.archiveFilePath(sz.ext)

Expand All @@ -40,12 +47,24 @@ func (sz *SevenZip) options() (opts []string) {
opts = append(opts, "-mhe=on")
}

// Get compression method (e.g., LZMA, LZMA2, PPMd, BZip2, Deflate, Copy)
method := sz.viper.GetString("method")
if len(method) > 0 {
opts = append(opts, fmt.Sprintf("-m0=%s", method))
}

// Get compression level (0-9)
compressionLevel := sz.viper.GetInt("compression_level")
if sz.viper.IsSet("compression_level") && compressionLevel >= 0 && compressionLevel <= 9 {
opts = append(opts, fmt.Sprintf("-mx=%d", compressionLevel))
}

// Get volume size for split archives (e.g., "100m", "1g")
volumeSize := sz.viper.GetString("volume_size")
if len(volumeSize) > 0 {
opts = append(opts, fmt.Sprintf("-v%s", volumeSize))
}

// Get additional args
args := sz.viper.GetString("args")
if len(args) > 0 {
Expand Down
96 changes: 96 additions & 0 deletions compressor/7z_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,99 @@ func TestSevenZip_optionsWithAllOptions(t *testing.T) {
assert.Equal(t, opts[3], "-mx=5")
assert.Equal(t, opts[4], "-mmt=4")
}

func TestSevenZip_optionsWithMethod(t *testing.T) {
v := viper.New()
v.Set("method", "LZMA2")
base := newBase(config.ModelConfig{
CompressWith: config.SubConfig{
Type: "7z",
Name: "7z",
Viper: v,
},
})

sz := &SevenZip{base}
opts := sz.options()
assert.Equal(t, opts[0], "a")
assert.Equal(t, opts[1], "-m0=LZMA2")
}

func TestSevenZip_optionsWithVolumeSize(t *testing.T) {
v := viper.New()
v.Set("volume_size", "100m")
base := newBase(config.ModelConfig{
CompressWith: config.SubConfig{
Type: "7z",
Name: "7z",
Viper: v,
},
})

sz := &SevenZip{base}
opts := sz.options()
assert.Equal(t, opts[0], "a")
assert.Equal(t, opts[1], "-v100m")
}

func TestSevenZip_optionsWithMethodAndVolumeSize(t *testing.T) {
v := viper.New()
v.Set("method", "PPMd")
v.Set("volume_size", "1g")
base := newBase(config.ModelConfig{
CompressWith: config.SubConfig{
Type: "7z",
Name: "7z",
Viper: v,
},
})

sz := &SevenZip{base}
opts := sz.options()
assert.Equal(t, opts[0], "a")
assert.Equal(t, opts[1], "-m0=PPMd")
assert.Equal(t, opts[2], "-v1g")
}

func TestSevenZip_HasVolumeSize(t *testing.T) {
v := viper.New()
base := newBase(config.ModelConfig{
CompressWith: config.SubConfig{
Type: "7z",
Name: "7z",
Viper: v,
},
})

sz := &SevenZip{base}
assert.Equal(t, sz.HasVolumeSize(), false)

v.Set("volume_size", "100m")
assert.Equal(t, sz.HasVolumeSize(), true)
}

func TestSevenZip_optionsWithAllNewOptions(t *testing.T) {
v := viper.New()
v.Set("password", "secret")
v.Set("method", "LZMA2")
v.Set("compression_level", 9)
v.Set("volume_size", "500m")
v.Set("args", "-mmt=4")
base := newBase(config.ModelConfig{
CompressWith: config.SubConfig{
Type: "7z",
Name: "7z",
Viper: v,
},
})

sz := &SevenZip{base}
opts := sz.options()
assert.Equal(t, opts[0], "a")
assert.Equal(t, opts[1], "-psecret")
assert.Equal(t, opts[2], "-mhe=on")
assert.Equal(t, opts[3], "-m0=LZMA2")
assert.Equal(t, opts[4], "-mx=9")
assert.Equal(t, opts[5], "-v500m")
assert.Equal(t, opts[6], "-mmt=4")
}
7 changes: 6 additions & 1 deletion compressor/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,12 @@ func Run(model config.ModelConfig) (string, error) {

// Select compressor based on type
if model.CompressWith.Type == "7z" || model.CompressWith.Type == "7zip" {
c = &SevenZip{Base: base}
sz := &SevenZip{Base: base}
// Check for conflict: 7z native volume splitting and external splitter cannot be used together
if sz.HasVolumeSize() && model.Splitter != nil {
return "", fmt.Errorf("cannot use both 7z native volume splitting (volume_size) and external splitter (split_with) at the same time")
}
c = sz
} else {
c = &Tar{Base: base}
}
Expand Down
29 changes: 29 additions & 0 deletions compressor/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,32 @@ func TestBaseInterface(t *testing.T) {
assert.Equal(t, result, "aaa")
assert.Nil(t, err)
}

func TestRun_SevenZipVolumeSizeWithSplitter_ReturnsError(t *testing.T) {
compressViper := viper.New()
compressViper.Set("type", "7z")
compressViper.Set("volume_size", "100m")
compressViper.SetDefault("filename_format", "2006.01.02.15.04.05")

splitterViper := viper.New()
splitterViper.Set("chunk_size", "50m")

modelViper := viper.New()

model := config.ModelConfig{
Name: "test",
TempPath: "/tmp",
DumpPath: "/tmp/test",
CompressWith: config.SubConfig{
Type: "7z",
Name: "7z",
Viper: compressViper,
},
Splitter: splitterViper,
Viper: modelViper,
}

_, err := Run(model)
assert.NotNil(t, err)
assert.True(t, strings.Contains(err.Error(), "cannot use both 7z native volume splitting"))
}