Repository: amzn/ion-go
Pull request #85 (2020-06-23 19:50:55)

Add InStruct() to Reader and Writer API
InStruct() needs to be in the API so IonHash can use it.



Armanbqt: This is to address #71 
Please let me know if there are any issues with changes of the API.

Patch:
diff --git a/cmd/ion-go/eventwriter.go b/cmd/ion-go/eventwriter.go
index 3f6c570..40f9240 100644
--- a/cmd/ion-go/eventwriter.go
+++ b/cmd/ion-go/eventwriter.go
@@ -15,6 +15,7 @@ type eventwriter struct {
 	depth       int
 	fieldname   string
 	annotations []string
+	inStruct    map[int]bool
 }
 
 // NewEventWriter creates an ion.Writer that writes out a sequence
@@ -194,10 +195,12 @@ func (e *eventwriter) BeginStruct() error {
 		return err
 	}
 	e.depth++
+	e.inStruct[e.depth] = true
 	return nil
 }
 
 func (e *eventwriter) EndStruct() error {
+	e.inStruct[e.depth] = false
 	e.depth--
 	return e.write(event{
 		EventType: containerEnd,
@@ -212,6 +215,10 @@ func (e *eventwriter) Finish() error {
 	return e.enc.Finish()
 }
 
+func (e *eventwriter) IsInStruct() bool {
+	return e.inStruct[e.depth] == true
+}
+
 func stringify(val interface{}) string {
 	bs, err := ion.MarshalText(val)
 	if err != nil {
diff --git a/cmd/ion-go/nopwriter.go b/cmd/ion-go/nopwriter.go
index 10d1822..50e1a91 100644
--- a/cmd/ion-go/nopwriter.go
+++ b/cmd/ion-go/nopwriter.go
@@ -105,3 +105,7 @@ func (nopwriter) EndStruct() error {
 func (nopwriter) Finish() error {
 	return nil
 }
+
+func (nopwriter) IsInStruct() bool {
+	return false
+}
diff --git a/ion/binarywriter.go b/ion/binarywriter.go
index e6df271..d3b6e06 100644
--- a/ion/binarywriter.go
+++ b/ion/binarywriter.go
@@ -448,7 +448,7 @@ func (w *binaryWriter) beginValue(api string) error {
 		}
 	}
 
-	if w.InStruct() {
+	if w.IsInStruct() {
 		if name == "" {
 			return &UsageError{api, "field name not set"}
 		}
diff --git a/ion/reader.go b/ion/reader.go
index 9f16c5a..f3c9255 100644
--- a/ion/reader.go
+++ b/ion/reader.go
@@ -13,7 +13,7 @@ import (
 // A Reader reads a stream of Ion values.
 //
 // The Reader has a logical position within the stream of values, influencing the
-// values returnedd from its methods. Initially, the Reader is positioned before the
+// values returned from its methods. Initially, the Reader is positioned before the
 // first value in the stream. A call to Next advances the Reader to the first value
 // in the stream, with subsequent calls advancing to subsequent values. When a call to
 // Next moves the Reader to the position after the final value in the stream, it returns
@@ -148,6 +148,9 @@ type Reader interface {
 	// ByteValue returns the current value as a byte slice (if that makes sense). It returns
 	// an error if the current value is not an Ion clob or an Ion blob.
 	ByteValue() ([]byte, error)
+
+	// IsInStruct indicates if the reader is currently positioned in a struct.
+	IsInStruct() bool
 }
 
 // NewReader creates a new Ion reader of the appropriate type by peeking
@@ -386,3 +389,8 @@ func (r *reader) clear() {
 	r.valueType = NoType
 	r.value = nil
 }
+
+// IsInStruct returns true if we are currently in a struct.
+func (r *reader) IsInStruct() bool {
+	return r.ctx.peek() == ctxInStruct
+}
diff --git a/ion/textwriter.go b/ion/textwriter.go
index 70bcdbf..17c23d8 100644
--- a/ion/textwriter.go
+++ b/ion/textwriter.go
@@ -307,7 +307,7 @@ func (w *textWriter) beginValue(api string) error {
 		}
 	}
 
-	if w.InStruct() {
+	if w.IsInStruct() {
 		if err := w.writeFieldName(api); err != nil {
 			return err
 		}
diff --git a/ion/writer.go b/ion/writer.go
index 6d1674f..83d6133 100644
--- a/ion/writer.go
+++ b/ion/writer.go
@@ -110,6 +110,9 @@ type Writer interface {
 
 	// Finish finishes writing values and flushes any buffered data.
 	Finish() error
+
+	// IsInStruct indicates if we are currently writing a struct or not.
+	IsInStruct() bool
 }
 
 // A writer holds shared stuff for all writers.
@@ -128,7 +131,7 @@ func (w *writer) FieldName(val string) error {
 	if w.err != nil {
 		return w.err
 	}
-	if !w.InStruct() {
+	if !w.IsInStruct() {
 		w.err = errors.New("ion: Writer.FieldName called when not writing a struct")
 		return w.err
 	}
@@ -153,8 +156,8 @@ func (w *writer) Annotations(val ...string) error {
 	return w.err
 }
 
-// InStruct returns true if we're currently writing a struct.
-func (w *writer) InStruct() bool {
+// IsInStruct returns true if we're currently writing a struct.
+func (w *writer) IsInStruct() bool {
 	return w.ctx.peek() == ctxInStruct
 }
 


Test patch:
diff --git a/ion/textreader_test.go b/ion/textreader_test.go
index 68a84ba..f3c5816 100644
--- a/ion/textreader_test.go
+++ b/ion/textreader_test.go
@@ -423,6 +423,38 @@ func TestTrsToString(t *testing.T) {
 	}
 }
 
+func TestInStruct(t *testing.T) {
+	r := NewReaderStr("[ { a:() } ]")
+
+	r.Next()
+	r.StepIn() // In the list, before the struct
+	if r.IsInStruct() {
+		t.Fatal("IsInStruct returned true before we were in a struct")
+	}
+
+	r.Next()
+	r.StepIn() // In the struct
+	if !r.IsInStruct() {
+		t.Fatal("We were in a struct, IsInStruct should have returned true")
+	}
+
+	r.Next()
+	r.StepIn() // In the Sexp
+	if r.IsInStruct() {
+		t.Fatal("IsInStruct returned true before we were in a struct")
+	}
+
+	r.StepOut() // Out of the Sexp, back in the struct again
+	if !r.IsInStruct() {
+		t.Fatal("We were in a struct, IsInStruct should have returned true")
+	}
+
+	r.StepOut() // out of struct, back in the list again
+	if r.IsInStruct() {
+		t.Fatal("IsInStruct returned true before we were in a struct")
+	}
+}
+
 type containerhandler func(t *testing.T, r Reader)
 
 func _sexp(t *testing.T, r Reader, f containerhandler) {