diff --git a/compressor/7z.go b/compressor/7z.go index 518d110..b40b981 100644 --- a/compressor/7z.go +++ b/compressor/7z.go @@ -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) @@ -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 { diff --git a/compressor/7z_test.go b/compressor/7z_test.go index 7faf93d..5cd9a09 100644 --- a/compressor/7z_test.go +++ b/compressor/7z_test.go @@ -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") +} diff --git a/compressor/base.go b/compressor/base.go index 7603088..63fbc88 100644 --- a/compressor/base.go +++ b/compressor/base.go @@ -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} } diff --git a/compressor/base_test.go b/compressor/base_test.go index 8095d40..9565777 100644 --- a/compressor/base_test.go +++ b/compressor/base_test.go @@ -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")) +}