Skip to content

Commit 5513af0

Browse files
authored
GH-44 - Enable value skipping (#47)
* Revert abstraction of start/end values, enable omit Rely on start/end tag values for unmarshalling again, enable omit indicator "-" * Add test action * Fix up test expectations using csv denomination Fix lint * Update godoc * Update template gen w/csv formatting, tag comments * Update lint action * "Tidy" global vals * Add omit array test * Add omit struct test * Update omit struct test
1 parent 0a1d689 commit 5513af0

File tree

10 files changed

+396
-193
lines changed

10 files changed

+396
-193
lines changed

.github/workflows/lint.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- uses: actions/checkout@v2.3.4
17-
- uses: golangci/golangci-lint-action@v2.5.2
17+
18+
- name: Run linter
19+
uses: golangci/golangci-lint-action@v2.5.2
1820
with:
1921
version: v1.33.2
22+
args: --timeout=5m
2023
env:
2124
GOFLAGS: "-mod=readonly"

.github/workflows/test.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Test
2+
on:
3+
push:
4+
branches:
5+
- master
6+
paths:
7+
- '**.go'
8+
pull_request:
9+
branches:
10+
- '**'
11+
paths:
12+
- '**.go'
13+
14+
jobs:
15+
lint:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v2.3.4
19+
20+
# Fetch all refs and full history for pull request analysis and blame information, respectively
21+
- run: git fetch --prune --unshallow
22+
23+
- name: "Run test coverage"
24+
uses: "docker://golang:1.16.5-stretch"
25+
env:
26+
GO111MODULE: "on"
27+
with:
28+
entrypoint: make
29+
args: test

.golangci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ linters:
163163
- misspell
164164
- nakedret
165165
- scopelint
166-
- staticcheck
166+
# - staticcheck action failing with non-reproduceable error locally
167167
- structcheck
168168
- typecheck
169169
- unconvert

cmd/pkg/template/template.go

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,25 @@ import (
1111
)
1212

1313
var (
14-
startPos = 1
15-
endPos = 1
16-
structs = make([]string, 0)
14+
// TODO: investigate https://github.com/masterminds/sprig for better
15+
// templating?
16+
//
17+
// Global state for funcMap reference when templating
18+
newStart = 1
19+
newEnd = 1
20+
savedStart = 1
21+
savedEnd = 1
22+
cursor = 1
23+
24+
structs = make([]string, 0)
1725

1826
special = regexp.MustCompile("[^a-zA-Z0-9]+")
1927
)
2028

2129
func getTemplateFuncs() template.FuncMap {
2230
return template.FuncMap{
2331
"goType": goType,
24-
"picTag": picTag,
32+
"picTag": newPicTag,
2533
"sanitiseName": sanitiseName,
2634
"indexComment": indexComment,
2735
"isStruct": isStruct,
@@ -62,8 +70,8 @@ type {{ .Root.Name }} struct {
6270
}
6371

6472
func Copybook() *template.Template {
65-
startPos = 1
66-
endPos = 1
73+
newStart = 1
74+
newEnd = 1
6775
structs = make([]string, 0)
6876

6977
return getTemplate()
@@ -94,14 +102,26 @@ func goType(l *lex.Record) string {
94102
return tag
95103
}
96104

97-
func picTag(l int, i int) string {
98-
if i > 0 {
99-
return "`" + fmt.Sprintf("pic:\"%d,%d\"", l, i) + "`"
105+
func newPicTag(length int, elemCount int) string {
106+
// tag values
107+
start := newStart
108+
size := length
109+
if elemCount > 0 {
110+
size *= elemCount
100111
}
101-
return "`" + fmt.Sprintf("pic:\"%d\"", l) + "`"
112+
end := start + (size - 1)
113+
114+
// manipulate global state :(
115+
newEnd = end
116+
newStart = newEnd + 1
117+
if elemCount > 0 {
118+
return "`" + fmt.Sprintf("pic:\"%d,%d,%d\"", start, end, elemCount) + "`"
119+
}
120+
121+
return "`" + fmt.Sprintf("pic:\"%d,%d\"", start, end) + "`"
102122
}
103123

104-
// FIXME: (pgmitche) index comments are being overcalculated now,
124+
// FIXME: (pgmitche) index comments are being over-calculated now,
105125
// due to struct support.
106126
// e.g. DUMMYGROUP1's length of 63 is being calculated 3x to 1+189
107127
// so DUMMYGROUP3 now starts at 201, instead of 64
@@ -110,16 +130,16 @@ func picTag(l int, i int) string {
110130
// DUMMYGROUP1 DUMMYGROUP1 `pic:"63"` // start:1 end:63
111131
// DUMMYGROUP3 DUMMYGROUP3 `pic:"201"` // start:190 end:390
112132
// }
113-
func indexComment(l int, i int) string {
114-
size := l
115-
if i > 0 {
116-
size *= i
133+
func indexComment(length int, elemCount int) string {
134+
size := length
135+
if elemCount > 0 {
136+
size *= elemCount
117137
}
118138

119-
s := startPos
120-
endPos += size
121-
startPos = endPos
122-
return fmt.Sprintf(" // start:%d end:%d", s, endPos-1)
139+
s := cursor
140+
e := s + size
141+
cursor = e
142+
return fmt.Sprintf(" // start:%d end:%d", s, e-1)
123143
}
124144

125145
func sanitiseName(s string) string {
@@ -137,6 +157,8 @@ func getStructs() []string {
137157
// FIXME: (pgmitche) if record is struct but has no children,
138158
// it should probably be ignored entirely
139159
func getStructTemplate() *template.Template {
160+
newStart = 1
161+
newEnd = 1
140162
t, err := template.New("struct").
141163
Funcs(getTemplateFuncs()).
142164
Parse(`
@@ -161,11 +183,15 @@ type {{ sanitiseName .Name }} struct {
161183
// FIXME: (pgmitche) if record is struct but has no children,
162184
// it should probably be ignored entirely
163185
func buildStruct(r *lex.Record) string {
186+
savedStart = newStart
187+
savedEnd = newEnd
164188
b := bytes.Buffer{}
165189
if err := getStructTemplate().Execute(&b, r); err != nil {
166190
panic(err)
167191
}
168192

169193
structs = append(structs, b.String())
194+
newStart = savedStart
195+
newEnd = savedEnd
170196
return ""
171197
}

cmd/pkg/template/template_test.go

Lines changed: 0 additions & 1 deletion
This file was deleted.

decode.go

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import (
1212
// Example
1313
//
1414
// type Company struct {
15-
// Name string `pic:"30"` //30 chars, 0-30
16-
// Employees int `pic:"9"` //9 chars, 31-39
17-
// BusinessRegistrationNumber string `pic:"12"` //12 chars 40-51
15+
// Name string `pic:"1,30"` // 30 chars, 1-30
16+
// Employees int `pic:"31,39"` // 9 chars, 31-39
17+
// BusinessRegistrationNumber string `pic:"40,51"` // 12 chars 40-51
1818
// }
1919
// s := `HERARE30CHARS OFCOMPANYNAME999999999RegistrtnNum`
2020
// c := &Company{}
@@ -53,57 +53,66 @@ func (d *decoder) Decode(v interface{}) error {
5353
}
5454

5555
if rv.Elem().Kind() == reflect.Slice {
56-
return d.scanLines(rv.Elem())
56+
return d.decodeLines(rv.Elem())
5757
}
5858

59-
ok, err := d.scanLine(rv)
59+
ok, err := d.decodeLine(rv)
6060
if d.done && err == nil && !ok {
6161
return io.EOF
6262
}
6363

6464
return err
6565
}
6666

67-
func (d *decoder) scanLine(v reflect.Value) (bool, error) {
67+
// decodeLine scans the next line in the scanner, sets d.done to true if the
68+
// scanner has reached EOF (and returns false indicating not to continue),
69+
// otherwise, detecting the appropriate setter function based on the type of the
70+
// given value v, and unpacks a string representation of the bytes associated
71+
// with that line, into the object v using the identified setter function, set.
72+
func (d *decoder) decodeLine(v reflect.Value) (bool, error) {
6873
if ok := d.s.Scan(); !ok {
6974
d.done = true
7075
return false, nil
7176
}
7277

73-
t := v.Type()
74-
75-
set := newSetFunc(t, 0, 0)
78+
set := newSetFunc(v.Type(), 0, 0)
7679
return true, set(v, string(d.s.Bytes()))
7780
}
7881

79-
func (d *decoder) scanLines(v reflect.Value) (err error) {
80-
ct := v.Type().Elem()
82+
// decodeLines iterates through each line of the scanner, builds an instance of
83+
// the type that the line is expected to be unpacked into, sets the type to the
84+
// scanned values, and appends it to the incoming object v
85+
func (d *decoder) decodeLines(v reflect.Value) (err error) {
86+
currentType := v.Type().Elem()
8187
for {
82-
nv := reflect.New(ct).Elem()
83-
ok, err := d.scanLine(nv)
88+
newValue := reflect.New(currentType).Elem()
89+
ok, err := d.decodeLine(newValue)
8490
if err != nil {
8591
return err
8692
}
8793

8894
if ok {
89-
v.Set(reflect.Append(v, nv))
95+
v.Set(reflect.Append(v, newValue))
9096
}
9197

9298
if d.done {
9399
break
94100
}
95101
}
102+
96103
return nil
97104
}
98105

99-
func newValFromLine(s string, start int, end int) string {
100-
if len(s) == 0 || start > len(s) {
106+
// newValFromLine cuts a value out of a string line from character indices
107+
// start to end
108+
func newValFromLine(line string, start int, end int) string {
109+
if len(line) == 0 || start > len(line) {
101110
return ""
102111
}
103112

104-
if end > len(s) {
105-
end = len(s)
113+
if end > len(line) {
114+
end = len(line)
106115
}
107116

108-
return strings.Trim(s[start-1:end], " ")
117+
return strings.Trim(line[start-1:end], " ")
109118
}

0 commit comments

Comments
 (0)