Skip to content

Commit

Permalink
feat(tpl): add stencil.ReadBlocks to parse blocks from arbitrary files (
Browse files Browse the repository at this point in the history
#134)

* feat(tpl): add stencil.ReadBlocks to parse blocks from arbitrary files

* PR feedback
  • Loading branch information
jaredallard authored Sep 5, 2022
1 parent 21cfb36 commit b9e3141
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 0 deletions.
30 changes: 30 additions & 0 deletions docs/content/en/functions/stencil.ReadBlocks.md
Original file line number Diff line number Diff line change
@@ -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 }}
```


38 changes: 38 additions & 0 deletions internal/codegen/tpl_stencil.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
68 changes: 68 additions & 0 deletions internal/codegen/tpl_stencil_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}

0 comments on commit b9e3141

Please sign in to comment.