From 0385eadc08a1f327bae39071c2998fadd176eac2 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 12 Dec 2023 01:56:43 +0500 Subject: [PATCH] impr: support binary files --- internal/engine/docker.go | 2 +- internal/fileio/fileio.go | 26 ++++++++++++++++ internal/fileio/fileio_test.go | 56 ++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/internal/engine/docker.go b/internal/engine/docker.go index ae20622..66ef02d 100644 --- a/internal/engine/docker.go +++ b/internal/engine/docker.go @@ -139,7 +139,7 @@ func (e *Docker) writeFiles(dir string, files Files) error { name = e.cmd.Entry } path := filepath.Join(dir, name) - err = os.WriteFile(path, []byte(content), 0444) + err = fileio.WriteFile(path, content, 0444) return err == nil }) return err diff --git a/internal/fileio/fileio.go b/internal/fileio/fileio.go index 6b672cb..6919e28 100644 --- a/internal/fileio/fileio.go +++ b/internal/fileio/fileio.go @@ -2,10 +2,14 @@ package fileio import ( + "encoding/base64" "encoding/json" + "errors" "io" + "io/fs" "os" "path/filepath" + "strings" ) // CopyFile copies all files matching the pattern @@ -52,3 +56,25 @@ func ReadJson[T any](path string) (T, error) { } return obj, err } + +// WriteFile writes the file to disk. +// The content can be text or binary (encoded as a data URL), +// e.g. data:application/octet-stream;base64,MTIz +func WriteFile(path, content string, perm fs.FileMode) (err error) { + var data []byte + if strings.HasPrefix(content, "data:") { + // data-url encoded file + _, encoded, found := strings.Cut(content, ",") + if !found { + return errors.New("invalid data-url encoding") + } + data, err = base64.StdEncoding.DecodeString(encoded) + if err != nil { + return err + } + } else { + // text file + data = []byte(content) + } + return os.WriteFile(path, data, perm) +} diff --git a/internal/fileio/fileio_test.go b/internal/fileio/fileio_test.go index c4513ac..846dc1e 100644 --- a/internal/fileio/fileio_test.go +++ b/internal/fileio/fileio_test.go @@ -82,3 +82,59 @@ func TestReadJson(t *testing.T) { } }) } + +func TestWriteFile(t *testing.T) { + dir, err := os.MkdirTemp("", "files") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + t.Run("text", func(t *testing.T) { + path := filepath.Join(dir, "data.txt") + err = WriteFile(path, "hello", 0444) + if err != nil { + t.Fatalf("expected nil err, got %v", err) + } + got, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read file: expected nil err, got %v", err) + } + want := []byte("hello") + if !reflect.DeepEqual(got, want) { + t.Errorf("read file: expected %v, got %v", want, got) + } + }) + + t.Run("binary", func(t *testing.T) { + path := filepath.Join(dir, "data.bin") + err = WriteFile(path, "data:application/octet-stream;base64,MTIz", 0444) + if err != nil { + t.Fatalf("expected nil err, got %v", err) + } + got, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read file: expected nil err, got %v", err) + } + want := []byte("123") + if !reflect.DeepEqual(got, want) { + t.Errorf("read file: expected %v, got %v", want, got) + } + }) + + t.Run("missing data-url separator", func(t *testing.T) { + path := filepath.Join(dir, "data.bin") + err = WriteFile(path, "data:application/octet-stream:MTIz", 0444) + if err == nil { + t.Fatal("expected error, got nil") + } + }) + + t.Run("invalid binary value", func(t *testing.T) { + path := filepath.Join(dir, "data.bin") + err = WriteFile(path, "data:application/octet-stream;base64,12345", 0444) + if err == nil { + t.Fatal("expected error, got nil") + } + }) +}