diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 86c0b09..a5dacde 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -1,6 +1,8 @@ package cli import ( + "bytes" + "compress/gzip" "encoding/json" "fmt" "io" @@ -224,7 +226,13 @@ func WriteToTmpFile(data []byte) (string, error) { func writeToFile(data []byte, file *os.File) (string, error) { logger.Info("Saving results to %s", file.Name()) - _, err := file.Write(data) + compressedData, err := GzipCompress(data) + if err != nil { + logger.Error("Output file write failed: %v", err) + return "", err + } + + _, err = file.Write(compressedData) if err != nil { logger.Error("Output file write failed: %v", err) return "", err @@ -390,18 +398,23 @@ func MergeFiles(path string, cmd *cobra.Command) (*parser.Result, error) { // Load ... func Load(path string) (*parser.Result, error) { var result parser.Result - jsonFile, err := os.Open(path) // #nosec + jsonFile, err := os.Open(path) if err != nil { return nil, err } - defer jsonFile.Close() // #nosec + defer jsonFile.Close() bytes, err := io.ReadAll(jsonFile) if err != nil { return nil, err } - err = json.Unmarshal(bytes, &result) + decompressedBytes, err := GzipDecompress(bytes) + if err != nil { + return nil, err + } + + err = json.Unmarshal(decompressedBytes, &result) if err != nil { return nil, err @@ -409,3 +422,51 @@ func Load(path string) (*parser.Result, error) { return &result, nil } + +func IsGzipCompressed(bytes []byte) bool { + if len(bytes) < 2 { + return false + } + + return bytes[0] == 0x1f && bytes[1] == 0x8b +} + +// takes a slice of data bytes, compresses it and replaces with compressed bytes +func GzipCompress(data []byte) ([]byte, error) { + var buf bytes.Buffer + + writer := gzip.NewWriter(&buf) + + _, err := writer.Write(data) + if err != nil { + return data, err + } + + err = writer.Close() + if err != nil { + return data, err + } + + return buf.Bytes(), nil +} + +func GzipDecompress(data []byte) ([]byte, error) { + if !IsGzipCompressed(data) { + return data, nil + } + + reader, err := gzip.NewReader(bytes.NewReader(data)) + if err != nil { + logger.Error("Decompression failed: %v", err) + return data, err + } + defer reader.Close() + + newData, err := io.ReadAll(reader) + if err != nil { + logger.Error("Decompression failed: %v", err) + return data, err + } + + return newData, nil +} diff --git a/pkg/cli/cli_test.go b/pkg/cli/cli_test.go index cfb81cf..e98a2ec 100644 --- a/pkg/cli/cli_test.go +++ b/pkg/cli/cli_test.go @@ -15,7 +15,6 @@ import ( ) func Test_LoadFiles(t *testing.T) { - t.Run("with invalid path to file", func(t *testing.T) { filePath := generateFile(t) paths, err := cli.LoadFiles([]string{fmt.Sprintf("%s1", filePath)}, ".xml") @@ -197,3 +196,58 @@ func TestWriteToFilePath(t *testing.T) { } }) } + +func Test_IsGzipCompressed(t *testing.T) { + testCases := []struct { + Name string + Input string + Want bool + }{ + { + Name: "Empty input", + Input: ``, + Want: false, + }, + { + Name: "Empty compressed input", + Input: string([]byte{0x1f, 0x8b, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff}), + Want: true, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("IsGzipCompressed(%s)", tc.Name), func(t *testing.T) { + assert.Equal(t, tc.Want, cli.IsGzipCompressed([]byte(tc.Input))) + }) + } +} + +func Test_GzipCompression(t *testing.T) { + testCases := []struct { + Name string + Input string + }{ + { + Name: "Empty input", + Input: ``, + }, + { + Name: "Some text", + Input: `Some text`, + }, + { + Name: "Some bytes", + Input: string([]byte{0x1, 0x2, 0x3, 0x4, 0x5}), + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("Compress -> Decompress is working (%s)", tc.Name), func(t *testing.T) { + compressed, err := cli.GzipCompress([]byte(tc.Input)) + assert.NoError(t, err) + decompressed, err := cli.GzipDecompress(compressed) + assert.NoError(t, err) + assert.Equal(t, tc.Input, string(decompressed)) + }) + } +}