Repository: openconfig/goyang
Pull request #238 (2022-11-29 01:11:53)

Add ParseOption/DeviateOption to ignore not-supported statements
```go
// DeviateOptions contains options for how deviations are handled.
type DeviateOptions struct {
	// IgnoreDeviateNotSupported indicates to the parser to retain nodes
	// that are marked with "deviate not-supported". An example use case is
	// where the user wants to interact with different targets that have
	// different support for a leaf without having to use a second instance
	// of an AST.
	IgnoreDeviateNotSupported bool
}
```

coveralls: 
[![Coverage Status](https://coveralls.io/builds/54638102/badge)](https://coveralls.io/builds/54638102)

Coverage decreased (-0.02%) to 84.006% when pulling **c71ae34b2f0cd84635ab216546d3e5e8129b33a8 on ignore-not-supported** into **03fa68fa7e16b556e953fe7d52d281e9c1e4ac81 on master**.


Patch:
diff --git a/pkg/yang/entry.go b/pkg/yang/entry.go
index 7dd8bfd..eb06437 100644
--- a/pkg/yang/entry.go
+++ b/pkg/yang/entry.go
@@ -1098,7 +1098,7 @@ func (e *Entry) Augment(addErrors bool) (processed, skipped int) {
 
 // ApplyDeviate walks the deviations within the supplied entry, and applies them to the
 // schema.
-func (e *Entry) ApplyDeviate() []error {
+func (e *Entry) ApplyDeviate(deviateOpts ...DeviateOpt) []error {
 	var errs []error
 	appendErr := func(err error) { errs = append(errs, err) }
 	for _, d := range e.Deviations {
@@ -1168,7 +1168,9 @@ func (e *Entry) ApplyDeviate() []error {
 						appendErr(fmt.Errorf("%s: node %s does not have a valid parent, but deviate not-supported references one", Source(e.Node), e.Name))
 						continue
 					}
-					dp.delete(deviatedNode.Name)
+					if !hasIgnoreDeviateNotSupported(deviateOpts) {
+						dp.delete(deviatedNode.Name)
+					}
 				case DeviationDelete:
 					if devSpec.Config != TSUnset {
 						deviatedNode.Config = TSUnset
diff --git a/pkg/yang/modules.go b/pkg/yang/modules.go
index 162a440..1d47a4e 100644
--- a/pkg/yang/modules.go
+++ b/pkg/yang/modules.go
@@ -395,7 +395,7 @@ func (ms *Modules) Process() []error {
 		for _, m := range devmods {
 			e := ToEntry(m)
 			if !dvP[e.Name] {
-				errs = append(errs, e.ApplyDeviate()...)
+				errs = append(errs, e.ApplyDeviate(ms.ParseOptions.DeviateOptions)...)
 				dvP[e.Name] = true
 			}
 		}
diff --git a/pkg/yang/options.go b/pkg/yang/options.go
index 879ab4e..2de2ebd 100644
--- a/pkg/yang/options.go
+++ b/pkg/yang/options.go
@@ -27,4 +27,33 @@ type Options struct {
 	// generated within the schema to store the logical grouping from which it
 	// is derived.
 	StoreUses bool
+	// DeviateOptions contains options for how deviations are handled.
+	DeviateOptions DeviateOptions
+}
+
+// DeviateOptions contains options for how deviations are handled.
+type DeviateOptions struct {
+	// IgnoreDeviateNotSupported indicates to the parser to retain nodes
+	// that are marked with "deviate not-supported". An example use case is
+	// where the user wants to interact with different targets that have
+	// different support for a leaf without having to use a second instance
+	// of an AST.
+	IgnoreDeviateNotSupported bool
+}
+
+// IsDeviateOpt ensures that DeviateOptions satisfies the DeviateOpt interface.
+func (DeviateOptions) IsDeviateOpt() {}
+
+// DeviateOpt is an interface that can be used in function arguments.
+type DeviateOpt interface {
+	IsDeviateOpt()
+}
+
+func hasIgnoreDeviateNotSupported(opts []DeviateOpt) bool {
+	for _, o := range opts {
+		if opt, ok := o.(DeviateOptions); ok {
+			return opt.IgnoreDeviateNotSupported
+		}
+	}
+	return false
 }


Test patch:
diff --git a/pkg/yang/entry_test.go b/pkg/yang/entry_test.go
index d6a8c59..b2a5ae0 100644
--- a/pkg/yang/entry_test.go
+++ b/pkg/yang/entry_test.go
@@ -2957,6 +2957,7 @@ func TestDeviation(t *testing.T) {
 	tests := []struct {
 		desc                    string
 		inFiles                 map[string]string
+		inParseOptions          Options
 		wants                   map[string][]deviationTest
 		wantParseErrSubstring   string
 		wantProcessErrSubstring string
@@ -3161,6 +3162,32 @@ func TestDeviation(t *testing.T) {
 				entry: &Entry{Name: "survivor"},
 			}},
 		},
+	}, {
+		desc:    "deviation - not supported but ignored by option",
+		inFiles: map[string]string{"deviate": mustReadFile(filepath.Join("testdata", "deviate-notsupported.yang"))},
+		inParseOptions: Options{
+			DeviateOptions: DeviateOptions{
+				IgnoreDeviateNotSupported: true,
+			},
+		},
+		wants: map[string][]deviationTest{
+			"deviate": {{
+				path:  "/target",
+				entry: &Entry{Name: "target"},
+			}, {
+				path:  "/target-list",
+				entry: &Entry{Name: "target-list"},
+			}, {
+				path:  "/a-leaf",
+				entry: &Entry{Name: "a-leaf"},
+			}, {
+				path:  "/a-leaflist",
+				entry: &Entry{Name: "a-leaflist"},
+			}, {
+				path:  "survivor",
+				entry: &Entry{Name: "survivor"},
+			}},
+		},
 	}, {
 		desc: "deviation removing non-existent node",
 		inFiles: map[string]string{
@@ -3527,6 +3554,7 @@ func TestDeviation(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.desc, func(t *testing.T) {
 			ms := NewModules()
+			ms.ParseOptions = tt.inParseOptions
 
 			for name, mod := range tt.inFiles {
 				if err := ms.Parse(mod, name); err != nil {