Skip to content

Commit 0af2eca

Browse files
authored
fixedlength file decl (#111)
1 parent ebe5496 commit 0af2eca

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package fixedlength
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/jf-tech/go-corelib/caches"
7+
)
8+
9+
type byHeaderFooterDecl struct {
10+
Header string `json:"header"`
11+
Footer string `json:"footer"`
12+
}
13+
14+
type columnDecl struct {
15+
Name string `json:"name"`
16+
StartPos int `json:"start_pos"` // 1-based. and rune-based.
17+
Length int `json:"length"` // rune-based length.
18+
Line *string `json:"line"`
19+
}
20+
21+
type envelopeDecl struct {
22+
Name *string `json:"name"`
23+
ByHeaderFooter *byHeaderFooterDecl `json:"by_header_footer"`
24+
ByRows *int `json:"by_rows"`
25+
NotTarget bool `json:"not_target"`
26+
Columns []*columnDecl `json:"columns"`
27+
}
28+
29+
type fileDecl struct {
30+
Envelopes []*envelopeDecl `json:"envelopes"`
31+
}
32+
33+
func (c *columnDecl) lineMatch(line []byte) bool {
34+
if c.Line == nil {
35+
return true
36+
}
37+
// validated in validation code.
38+
r, _ := caches.GetRegex(*c.Line)
39+
return r.Match(line)
40+
}
41+
42+
func (c *columnDecl) lineToColumn(line []rune) []rune {
43+
// StartPos is 1-based and its value >= 1 guaranteed by json schema validation done earlier.
44+
startPosZeroBased := c.StartPos - 1
45+
// If [startPosZeroBased, c.Length] is partially out of range, we'll return whatever is
46+
// in range; if [startPosZeroBased, c.Length] is fully out of range, we'll return "".
47+
switch {
48+
case startPosZeroBased+c.Length <= len(line):
49+
return line[startPosZeroBased : startPosZeroBased+c.Length]
50+
case startPosZeroBased < len(line):
51+
return line[startPosZeroBased:]
52+
}
53+
return nil
54+
}
55+
56+
func (e *envelopeDecl) byRows() int {
57+
if e.ByHeaderFooter != nil {
58+
panic(fmt.Sprintf("envelope '%s' type is not 'by_rows'", *e.Name))
59+
}
60+
if e.ByRows == nil {
61+
return 1
62+
}
63+
return *e.ByRows
64+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package fixedlength
2+
3+
import (
4+
"testing"
5+
6+
"github.com/jf-tech/go-corelib/strs"
7+
"github.com/jf-tech/go-corelib/testlib"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestColumnDecl_LineMatch(t *testing.T) {
12+
assert.True(t, (&columnDecl{}).lineMatch([]byte("test")))
13+
assert.False(t, (&columnDecl{Line: strs.StrPtr("^ABC.*$")}).lineMatch([]byte("test")))
14+
assert.True(t, (&columnDecl{Line: strs.StrPtr("^ABC.*$")}).lineMatch([]byte("ABCDEFG")))
15+
}
16+
17+
func TestColumnDecl_LineToColumn(t *testing.T) {
18+
decl := func(start, length int) *columnDecl {
19+
return &columnDecl{StartPos: start, Length: length}
20+
}
21+
assert.Nil(t, decl(10, 4).lineToColumn([]rune("test"))) // fully out of range
22+
assert.Equal(t, []rune("st"), decl(3, 4).lineToColumn([]rune("test"))) // partially out of range
23+
assert.Equal(t, []rune("tes"), decl(1, 3).lineToColumn([]rune("test"))) // fully in range
24+
}
25+
26+
func TestEnvelopeDecl_ByRows(t *testing.T) {
27+
assert.PanicsWithValue(t, `envelope 'env1' type is not 'by_rows'`, func() {
28+
(&envelopeDecl{Name: strs.StrPtr("env1"), ByHeaderFooter: &byHeaderFooterDecl{}}).byRows()
29+
})
30+
assert.Equal(t, 1, (&envelopeDecl{}).byRows())
31+
assert.Equal(t, 12, (&envelopeDecl{ByRows: testlib.IntPtr(12)}).byRows())
32+
}

0 commit comments

Comments
 (0)