Skip to content

Commit f1dc9b7

Browse files
committed
support true VARIES segment
1 parent 62ad1e4 commit f1dc9b7

22 files changed

+261
-34
lines changed

coder.go

-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package hl7
22

33
import (
44
"fmt"
5-
"reflect"
65
"strconv"
76
"strings"
87
)
@@ -19,9 +18,6 @@ type Registry interface {
1918

2019
const tagName = "hl7"
2120

22-
var empty []any
23-
var anyType = reflect.TypeOf(empty).Elem()
24-
2521
type structType byte
2622

2723
const (

coder_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,30 @@ func TestDecodeCompoundDateTime(t *testing.T) {
9999
t.Logf("MSH Date: %v", msg.DateTimeOfMessage)
100100
}
101101

102+
func TestVaries(t *testing.T) {
103+
raw, err := os.ReadFile(filepath.Join("testdata", "roundtrip", "vaers_long.hl7"))
104+
if err != nil {
105+
t.Fatal(err)
106+
}
107+
108+
d := NewDecoder(v25.Registry, nil)
109+
v, err := d.Decode(raw)
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
114+
vg := v.(v25.ORU_R01)
115+
for _, pr := range vg.PatientResult {
116+
for _, oo := range pr.OrderObservation {
117+
for _, o := range oo.Observation {
118+
for _, v := range o.OBX.ObservationValue {
119+
t.Logf("Type: %s, Value: %#v", o.OBX.ValueType, v)
120+
}
121+
}
122+
}
123+
}
124+
}
125+
102126
func printTypes(t *testing.T, list []any) {
103127
for _, item := range list {
104128
t.Logf("type %T", item)

decode.go

+39-17
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ type lineDecoder struct {
1818
readSep bool
1919

2020
unescaper *strings.Replacer
21-
22-
opt DecodeOption
2321
}
2422

2523
// Decode bytes into HL7 structures.
@@ -63,6 +61,16 @@ func (d *Decoder) DecodeGroup(list []any) (any, error) {
6361
return group(list, d.registry)
6462
}
6563

64+
// Varies should be implemented on a segment that knows how to
65+
// decode a child VARIES data type.
66+
type Varies interface {
67+
ChildVaries(dtReg map[string]any) (reflect.Value, error)
68+
}
69+
70+
type variesFunc func() (reflect.Value, error)
71+
72+
var variesType = reflect.TypeOf((*Varies)(nil)).Elem()
73+
6674
// Decode returns a list of segments without any grouping applied.
6775
func (d *Decoder) DecodeList(data []byte) ([]any, error) {
6876
// Explicitly accept both CR and LF as new lines. Some systems do use \n, despite the spec.
@@ -204,6 +212,14 @@ func (d *Decoder) DecodeList(data []byte) ([]any, error) {
204212
ff[index] = f
205213
}
206214

215+
var vfc variesFunc
216+
if rvv.Type().Implements(variesType) {
217+
dtReg := d.registry.DataType()
218+
vfc = func() (reflect.Value, error) {
219+
return rvv.Interface().(Varies).ChildVaries(dtReg)
220+
}
221+
}
222+
207223
for i, f := range ff {
208224
if i >= len(parts) {
209225
break
@@ -215,7 +231,7 @@ func (d *Decoder) DecodeList(data []byte) ([]any, error) {
215231
if f.tag.Omit {
216232
continue
217233
}
218-
err := ld.decodeSegmentList(p, f.tag, f.field)
234+
err := ld.decodeSegmentList(p, f.tag, f.field, vfc)
219235
if err != nil {
220236
return ret, fmt.Errorf("line %d, %s.%s: %w", lineNumber, SegmentName, f.name, err)
221237
}
@@ -238,7 +254,7 @@ func (d *lineDecoder) setupUnescaper() {
238254

239255
var timeType reflect.Type = reflect.TypeOf(time.Time{})
240256

241-
func (d *lineDecoder) decodeSegmentList(data []byte, t tag, rv reflect.Value) error {
257+
func (d *lineDecoder) decodeSegmentList(data []byte, t tag, rv reflect.Value, vfc variesFunc) error {
242258
if len(data) == 0 {
243259
return nil
244260
}
@@ -247,14 +263,14 @@ func (d *lineDecoder) decodeSegmentList(data []byte, t tag, rv reflect.Value) er
247263
if len(p) == 0 {
248264
continue
249265
}
250-
err := d.decodeSegment(p, t, rv, 1, len(parts) > 1)
266+
err := d.decodeSegment(p, t, rv, 1, len(parts) > 1, vfc)
251267
if err != nil {
252268
return fmt.Errorf("%s.%d: %w", rv.Type().String(), t.Order, err)
253269
}
254270
}
255271
return nil
256272
}
257-
func (d *lineDecoder) decodeSegment(data []byte, t tag, rv reflect.Value, level int, mustBeSlice bool) error {
273+
func (d *lineDecoder) decodeSegment(data []byte, t tag, rv reflect.Value, level int, mustBeSlice bool, vfc variesFunc) error {
258274
type field struct {
259275
tag tag
260276
field reflect.Value
@@ -269,20 +285,32 @@ func (d *lineDecoder) decodeSegment(data []byte, t tag, rv reflect.Value, level
269285
default:
270286
return fmt.Errorf("unknown field kind %v value=%v(%v) tag=%v data=%q", rv.Kind(), rv, rv.Type(), t, data)
271287
case reflect.Interface:
272-
// TODO: Support a true VARIES.
273-
return fmt.Errorf("unsupported interface field kind, data=%q", data)
288+
if vfc == nil {
289+
return fmt.Errorf("unsupported interface field kind %#v data=%q", t, data)
290+
}
291+
nextRV, err := vfc()
292+
if err != nil {
293+
return err
294+
}
295+
err = d.decodeSegment(data, t, nextRV, level, mustBeSlice, vfc)
296+
rv.Set(nextRV)
297+
return err
274298
case reflect.Pointer:
275299
next := reflect.New(rv.Type().Elem())
276300
rv.Set(next)
277-
return d.decodeSegment(data, t, next.Elem(), level, false)
301+
return d.decodeSegment(data, t, next.Elem(), level, false, vfc)
278302
case reflect.Slice:
279303
if len(data) == 0 {
280304
return nil
281305
}
282306
itemType := rv.Type().Elem()
307+
if itemType.Kind() == reflect.Uint8 {
308+
rv.SetBytes(data)
309+
return nil
310+
}
283311
itemValue := reflect.New(itemType)
284312
ivv := itemValue.Elem()
285-
err := d.decodeSegment(data, t, ivv, level, false)
313+
err := d.decodeSegment(data, t, ivv, level, false, vfc)
286314
if err != nil {
287315
return fmt.Errorf("slice: %w", err)
288316
}
@@ -357,7 +385,7 @@ func (d *lineDecoder) decodeSegment(data []byte, t tag, rv reflect.Value, level
357385
continue
358386
}
359387
f := ff[i]
360-
err := d.decodeSegment(p, f.tag, f.field, level+1, false)
388+
err := d.decodeSegment(p, f.tag, f.field, level+1, false, vfc)
361389
if err != nil {
362390
return fmt.Errorf("%s-%s.%d: %w", SegmentName, f.field.Type().String(), f.tag.Order, err)
363391
}
@@ -391,12 +419,6 @@ func (d *lineDecoder) decodeByte(v []byte, t tag) string {
391419
}
392420
return d.unescaper.Replace(string(v))
393421
}
394-
func (d *lineDecoder) decodeString(v string, t tag) string {
395-
if t.NoEscape {
396-
return v
397-
}
398-
return d.unescaper.Replace(v)
399-
}
400422

401423
func (d *lineDecoder) getID(data []byte) (string, int) {
402424
if d.readSep {

encode.go

+24
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,24 @@ func (e *Encoder) write(val string, level int, noEscape bool) {
308308
buf.WriteByte(c)
309309
}
310310
}
311+
func (e *Encoder) writeByte(val []byte, level int, noEscape bool) {
312+
if len(val) > 0 {
313+
e.flushDeferred(level)
314+
}
315+
buf := e.buf
316+
if noEscape {
317+
buf.Write(val)
318+
return
319+
}
320+
for i := 0; i < len(val); i++ {
321+
c := val[i]
322+
if esc, is := e.esc[c]; is {
323+
buf.Write(esc)
324+
continue
325+
}
326+
buf.WriteByte(c)
327+
}
328+
}
311329
func (e *Encoder) resetAllDeferred() {
312330
for _, d := range e.deferred {
313331
d.Reset()
@@ -415,6 +433,10 @@ func (e *Encoder) encodeDataType(t tag, o interface{}, level int) error {
415433
}
416434
}
417435
case reflect.Slice:
436+
if rv.Type().Elem().Kind() == reflect.Uint8 {
437+
e.writeByte(rv.Bytes(), level, true)
438+
return nil
439+
}
418440
ct := rv.Len()
419441
for i := 0; i < ct; i++ {
420442
if i != 0 {
@@ -430,6 +452,8 @@ func (e *Encoder) encodeDataType(t tag, o interface{}, level int) error {
430452
}
431453
return nil
432454
}
455+
case []byte:
456+
e.writeByte(v, level, true)
433457
case string:
434458
e.write(v, level, t.NoEscape)
435459
case time.Time:

h210/segment.go

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

h220/datatype.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

h220/segment.go

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

h231/datatype.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

h231/segment.go

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

h240/datatype.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

h240/segment.go

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

h250/datatype.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)