diff --git a/docs/content/en/functions/stencil.ReadBlocks.md b/docs/content/en/functions/stencil.ReadBlocks.md new file mode 100644 index 00000000..a09d719d --- /dev/null +++ b/docs/content/en/functions/stencil.ReadBlocks.md @@ -0,0 +1,30 @@ +--- +title: stencil.ReadBlocks +linktitle: stencil.ReadBlocks +description: > + +date: 2022-05-18 +categories: [functions] +menu: + docs: + parent: "functions" +--- + +ReadBlocks parses a file and attempts to read the blocks from it\, and their data\. + + +As a special case\, if the file does not exist\, an empty map is returned instead of an error\. + + +\*\*NOTE\*\*: This function does not guarantee that blocks are able to be read during runtime\. for example\, if you try to read the blocks of a file from another module there is no guarantee that that file will exist before you run this function\. Nor is there the ability to tell stencil to do that \(stencil does not have any order guarantees\)\. Keep that in mind when using this function\. + + +```go-text-template +{{- $blocks := stencil.ReadBlocks "myfile.txt" }} +{{- range $name, $data := $blocks }} + {{- $name }} + {{- $data }} +{{- end }} +``` + + diff --git a/internal/codegen/tpl_stencil.go b/internal/codegen/tpl_stencil.go index d8623371..ed186d4b 100644 --- a/internal/codegen/tpl_stencil.go +++ b/internal/codegen/tpl_stencil.go @@ -176,3 +176,41 @@ func (s *TplStencil) ApplyTemplate(name string, dataSli ...interface{}) (string, return buf.String(), nil } + +// ReadBlocks parses a file and attempts to read the blocks from it, and their data. +// +// As a special case, if the file does not exist, an empty map is returned instead of an error. +// +// **NOTE**: This function does not guarantee that blocks are able to be read during runtime. +// for example, if you try to read the blocks of a file from another module there is no guarantee +// that that file will exist before you run this function. Nor is there the ability to tell stencil +// to do that (stencil does not have any order guarantees). Keep that in mind when using this function. +// +// {{- $blocks := stencil.ReadBlocks "myfile.txt" }} +// {{- range $name, $data := $blocks }} +// {{- $name }} +// {{- $data }} +// {{- end }} +func (s *TplStencil) ReadBlocks(fpath string) (map[string]string, error) { + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + + // ensure that the file is within the current directory + // and not attempting to escape it + if _, err := osfs.New(cwd).Stat(fpath); err != nil { + if errors.Is(err, os.ErrNotExist) { + return map[string]string{}, nil + } + + return nil, err + } + + data, err := parseBlocks(fpath) + if err != nil { + return nil, err + } + + return data, nil +} diff --git a/internal/codegen/tpl_stencil_test.go b/internal/codegen/tpl_stencil_test.go new file mode 100644 index 00000000..818ad0da --- /dev/null +++ b/internal/codegen/tpl_stencil_test.go @@ -0,0 +1,68 @@ +// Copyright 2022 Outreach Corporation. All Rights Reserved. + +// Description: This file contains the public API for templates +// for stencil + +package codegen + +import ( + "reflect" + "testing" + + "github.com/go-git/go-billy/v5" +) + +func TestTplStencil_ReadBlocks(t *testing.T) { + type fields struct { + } + type args struct { + fpath string + } + tests := []struct { + name string + fields fields + args args + want map[string]string + wantErr error + }{ + { + name: "should read blocks from a file", + args: args{ + fpath: "testdata/blocks-test.txt", + }, + want: map[string]string{ + "helloWorld": "Hello, world!", + "e2e": "content", + }, + }, + { + name: "should error on out of chroot path", + args: args{ + fpath: "../testdata/blocks-test.txt", + }, + wantErr: billy.ErrCrossedBoundary, + }, + { + name: "should return no data on non-existent file", + args: args{ + fpath: "testdata/does-not-exist.txt", + }, + want: map[string]string{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &TplStencil{} + got, err := s.ReadBlocks(tt.args.fpath) + + // String checking because errors.Is isn't working + if (tt.wantErr != nil) && err.Error() != tt.wantErr.Error() { + t.Errorf("TplStencil.ReadBlocks() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("TplStencil.ReadBlocks() = %v, want %v", got, tt.want) + } + }) + } +}