Skip to content

Commit 827c9ef

Browse files
authored
1) EDI doc 2) add ignore_crlf to edi schema 3) add default to edi schema 4) make edi err msg more useful 5) fix cli.sh (#129)
1) EDI doc 2) add `ignore_crlf` to edi schema 3) add `default` to edi schema 4) make edi err msg more useful 5) fix cli.sh
1 parent 6fc8075 commit 827c9ef

20 files changed

+4996
-28
lines changed

cli.sh

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/bin/bash
22
CUR_DIR=$(pwd)
3-
SCRIPT_DIR=$(dirname "$0")
4-
cd $SCRIPT_DIR && \
3+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
4+
cd "$SCRIPT_DIR" && \
55
go build \
6-
-o $CUR_DIR/op \
6+
-o "$CUR_DIR/op" \
77
-ldflags "-X main.gitCommit=$(git rev-parse HEAD) -X main.buildEpochSec=$(date +%s)" \
8-
$SCRIPT_DIR/cli/op.go
9-
cd $CUR_DIR && ./op "$@"
8+
"$SCRIPT_DIR/cli/op.go"
9+
cd "$CUR_DIR" && ./op "$@"

cli/cmd/transformCmd.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ var (
2222
Short: "Transforms input to desired output based on a schema.",
2323
Args: cobra.NoArgs,
2424
RunE: func(cmd *cobra.Command, _ []string) error {
25-
return doTransform()
25+
if err := doTransform(); err != nil {
26+
fmt.Println() // to sure cobra cli always write out "Error: ..." on a new line.
27+
return err
28+
}
29+
return nil
2630
},
2731
}
2832
schema string

doc/edi_in_depth.md

+697
Large diffs are not rendered by default.

doc/resources/edi_comp_delim.png

52.3 KB
Loading

doc/resources/edi_seg_structure.png

118 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"Records": null,
3-
"FinalErr": "input 'test' between character [1,6]: missing segment name"
3+
"FinalErr": "input 'test' at segment no.1 (char[1,6]): missing segment name"
44
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"Records": null,
3-
"FinalErr": "input 'test' between character [1,7]: unable to find element 'e2' on segment ''"
3+
"FinalErr": "input 'test' at segment no.1 (char[1,7]): unable to find element 'e2' on segment ''"
44
}

extensions/omniv21/fileformat/edi/.snapshots/TestRead-seg_min_not_satisfied_before_EOF,_failure

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"Records": [
33
"{'e1':'0','e2':'1','e3':'2'}"
44
],
5-
"FinalErr": "input 'test' between character [11,11]: segment 'IEA' needs min occur 1, but only got 0"
5+
"FinalErr": "input 'test' at segment no.3 (char[11,11]): segment 'IEA' needs min occur 1, but only got 0"
66
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"Records": null,
3-
"FinalErr": "input 'test' between character [7,7]: segment 'ISA' needs min occur 1, but only got 0"
3+
"FinalErr": "input 'test' at segment no.1 (char[7,7]): segment 'ISA' needs min occur 1, but only got 0"
44
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"Records": [
33
"{'e1':'0','e2':'1','e3':'2'}",
4-
"{'e1':'3','e2':'','e3':''}"
4+
"{'e1':'3','e2':'','e3':'x'}"
55
],
66
"FinalErr": "EOF"
77
}

extensions/omniv21/fileformat/edi/decl.go

+1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ type fileDecl struct {
55
ElemDelim string `json:"element_delimiter,omitempty"`
66
CompDelim *string `json:"component_delimiter,omitempty"`
77
ReleaseChar *string `json:"release_character,omitempty"`
8+
IgnoreCRLF bool `json:"ignore_crlf,omitempty"`
89
SegDecls []*segDecl `json:"segment_declarations,omitempty"`
910
}

extensions/omniv21/fileformat/edi/reader.go

+14-4
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ type ediReader struct {
101101
target *idr.Node
102102
targetXPath *xpath.Expr
103103
runeBegin, runeEnd int
104+
segCount int
104105
unprocessedRawSeg rawSeg
105106
}
106107

@@ -169,6 +170,7 @@ func runeCountAndHasOnlyCRLF(b []byte) (int, bool) {
169170

170171
var (
171172
crBytes = []byte("\r")
173+
lfBytes = []byte("\n")
172174
)
173175

174176
func (r *ediReader) getUnprocessedRawSeg() (rawSeg, error) {
@@ -189,6 +191,7 @@ func (r *ediReader) getUnprocessedRawSeg() (rawSeg, error) {
189191
token = b
190192
break
191193
}
194+
r.segCount++
192195
// We are here because:
193196
// 1. we find next token (i.e. segment), great, let's process it, OR
194197
// 2. r.scanner.Scan() returns false and it's EOF (note scanner never returns EOF, it just returns false
@@ -259,14 +262,16 @@ func (r *ediReader) rawSegToNode(segDecl *segDecl) (*idr.Node, error) {
259262
break
260263
}
261264
}
262-
if rawElemIndex < len(rawElems) || elemDecl.EmptyIfMissing {
265+
if rawElemIndex < len(rawElems) || elemDecl.EmptyIfMissing || elemDecl.Default != nil {
263266
elemN := idr.CreateNode(idr.ElementNode, elemDecl.Name)
264267
idr.AddChild(n, elemN)
265268
data := ""
266269
if rawElemIndex < len(rawElems) {
267270
data = string(strs.ByteUnescape(rawElems[rawElemIndex].data, r.releaseChar.b, true))
268271
rawElems[rawElemIndex].dateUnescaped = true
269272
rawElemIndex++
273+
} else if elemDecl.Default != nil {
274+
data = *elemDecl.Default
270275
}
271276
elemV := idr.CreateNode(idr.TextNode, data)
272277
idr.AddChild(elemN, elemV)
@@ -325,7 +330,7 @@ func (r *ediReader) segNext() error {
325330
// 'end' to 'begin' to make the error msg less confusing.
326331
r.runeBegin = r.runeEnd
327332
return ErrInvalidEDI(r.fmtErrStr("segment '%s' needs min occur %d, but only got %d",
328-
cur.segDecl.Name, cur.segDecl.minOccurs(), cur.occurred))
333+
strs.FirstNonBlank(cur.segDecl.fqdn, cur.segDecl.Name), cur.segDecl.minOccurs(), cur.occurred))
329334
}
330335
if len(r.stack) <= 1 {
331336
return nil
@@ -420,8 +425,8 @@ func (r *ediReader) FmtErr(format string, args ...interface{}) error {
420425
}
421426

422427
func (r *ediReader) fmtErrStr(format string, args ...interface{}) string {
423-
return fmt.Sprintf("input '%s' between character [%d,%d]: %s",
424-
r.inputName, r.runeBegin, r.runeEnd, fmt.Sprintf(format, args...))
428+
return fmt.Sprintf("input '%s' at segment no.%d (char[%d,%d]): %s",
429+
r.inputName, r.segCount, r.runeBegin, r.runeEnd, fmt.Sprintf(format, args...))
425430
}
426431

427432
const (
@@ -442,6 +447,10 @@ func NewReader(inputName string, r io.Reader, decl *fileDecl, targetXPath string
442447
elemDelim := newStrPtrByte(&decl.ElemDelim)
443448
compDelim := newStrPtrByte(decl.CompDelim)
444449
releaseChar := newStrPtrByte(decl.ReleaseChar)
450+
if decl.IgnoreCRLF {
451+
r = ios.NewBytesReplacingReader(r, crBytes, nil)
452+
r = ios.NewBytesReplacingReader(r, lfBytes, nil)
453+
}
445454
scanner := ios.NewScannerByDelim3(r, segDelim.b, releaseChar.b, scannerFlags, make([]byte, ReaderBufSize))
446455
targetXPathExpr, err := func() (*xpath.Expr, error) {
447456
if targetXPath == "" || targetXPath == "." {
@@ -463,6 +472,7 @@ func NewReader(inputName string, r io.Reader, decl *fileDecl, targetXPath string
463472
targetXPath: targetXPathExpr,
464473
runeBegin: 1,
465474
runeEnd: 1,
475+
segCount: 0,
466476
unprocessedRawSeg: newRawSeg(),
467477
}
468478
reader.growStack(stackEntry{

extensions/omniv21/fileformat/edi/reader_test.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func TestGetUnprocessedRawSeg(t *testing.T) {
189189
ElemDelim: ":",
190190
},
191191
expected: []result{
192-
{rawSeg: rawSeg{}, err: `input 'test' between character [1,1]: cannot read segment, err: read failure`},
192+
{rawSeg: rawSeg{}, err: `input 'test' at segment no.1 (char[1,1]): cannot read segment, err: read failure`},
193193
},
194194
},
195195
{
@@ -244,7 +244,7 @@ func TestGetUnprocessedRawSeg(t *testing.T) {
244244
ElemDelim: "*",
245245
},
246246
expected: []result{
247-
{rawSeg: rawSeg{}, err: `input 'test' between character [1,2]: missing segment name`},
247+
{rawSeg: rawSeg{}, err: `input 'test' at segment no.1 (char[1,2]): missing segment name`},
248248
},
249249
},
250250
{
@@ -344,7 +344,7 @@ func TestRawSegToNode(t *testing.T) {
344344
},
345345
fqdn: "ISA",
346346
},
347-
err: `input 'test' between character [10,20]: unable to find element 'e3' on segment 'ISA'`,
347+
err: `input 'test' at segment no.0 (char[10,20]): unable to find element 'e3' on segment 'ISA'`,
348348
expected: "",
349349
},
350350
{
@@ -516,7 +516,7 @@ func TestSegDoneSegNext(t *testing.T) {
516516
target: nil,
517517
callSegDone: false,
518518
panicStr: "",
519-
err: `input 'test' between character [20,20]: segment 'C' needs min occur 1, but only got 0`,
519+
err: `input 'test' at segment no.0 (char[20,20]): segment 'C' needs min occur 1, but only got 0`,
520520
},
521521
{
522522
name: "root-A-C, C segDone, C over max, A becomes target, but r.target not nil",
@@ -632,7 +632,7 @@ func TestRead(t *testing.T) {
632632
"elements": [
633633
{ "name": "e1", "index": 1 },
634634
{ "name": "e2", "index": 2, "empty_if_missing": true },
635-
{ "name": "e3", "index": 3, "empty_if_missing": true }
635+
{ "name": "e3", "index": 3, "default": "x" }
636636
]
637637
}
638638
]
@@ -671,11 +671,12 @@ func TestRead(t *testing.T) {
671671
},
672672
{
673673
name: "2 seg groups, filtered target, success",
674-
input: "ISA*0*1*2\nISA*3*4*5\nISA*6*7*8\nIEA*6\n",
674+
input: "ISA*0*1*2|\nISA*3*4*5|\nISA*6*7*8|\r\nIEA*6|\n",
675675
declJSON: `
676676
{
677-
"segment_delimiter": "\n",
677+
"segment_delimiter": "|",
678678
"element_delimiter": "*",
679+
"ignore_crlf": true,
679680
"segment_declarations": [
680681
{
681682
"name": "isa_group",

extensions/omniv21/fileformat/edi/seg.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ const (
3030
)
3131

3232
type elem struct {
33-
Name string `json:"name,omitempty"`
34-
Index int `json:"index,omitempty"`
35-
CompIndex *int `json:"component_index,omitempty"`
36-
EmptyIfMissing bool `json:"empty_if_missing,omitempty"`
33+
Name string `json:"name,omitempty"`
34+
Index int `json:"index,omitempty"`
35+
CompIndex *int `json:"component_index,omitempty"`
36+
EmptyIfMissing bool `json:"empty_if_missing,omitempty"` // Deprecated, use Default
37+
Default *string `json:"default,omitempty"`
3738
}
3839

3940
func (e elem) compIndex() int {

0 commit comments

Comments
 (0)