Repository: google/yamlfmt (go)

Issue:
Multiple fields for formatter config can mess up default values
If the `basic` formatter is configured like so:
```
formatter:
  include_document_start: true
```
The default value of 2 for `Indent` is lost, and the indentation will end up being the `yaml.v3` default of 4.

Pull request:
basic: respect formatter config defaults
closes #23 

Under a certain configuration, the default value for `basic.Config.Indent`
would be ignored. Adjust the way the `NewWithConfig` function works to
decode the map overtop of default config, which will preserve default
values for fields not found in the map.

Patch:
diff --git a/Makefile b/Makefile
index 5766603..c693c40 100644
--- a/Makefile
+++ b/Makefile
@@ -11,4 +11,7 @@ addlicense:
 	addlicense -c "Google LLC" -l apache .
 
 test_diff:
-	go test -v -mod=mod github.com/google/yamlfmt/internal/diff
\ No newline at end of file
+	go test -v -mod=mod github.com/google/yamlfmt/internal/diff
+
+test_basic_formatter:
+	go test -v -mod=mod github.com/google/yamlfmt/formatters/basic
\ No newline at end of file
diff --git a/formatters/basic/factory.go b/formatters/basic/factory.go
index d64dca0..c3557a7 100644
--- a/formatters/basic/factory.go
+++ b/formatters/basic/factory.go
@@ -30,10 +30,10 @@ func (f *BasicFormatterFactory) NewDefault() yamlfmt.Formatter {
 }
 
 func (f *BasicFormatterFactory) NewWithConfig(configData map[string]interface{}) (yamlfmt.Formatter, error) {
-	var c Config
-	err := mapstructure.Decode(configData, &c)
+	config := DefaultConfig()
+	err := mapstructure.Decode(configData, &config)
 	if err != nil {
 		return nil, err
 	}
-	return &BasicFormatter{Config: &c}, nil
+	return &BasicFormatter{Config: config}, nil
 }


Test patch:
diff --git a/formatters/basic/factory_test.go b/formatters/basic/factory_test.go
new file mode 100644
index 0000000..a52fd7b
--- /dev/null
+++ b/formatters/basic/factory_test.go
@@ -0,0 +1,67 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package basic_test
+
+import (
+	"testing"
+
+	"github.com/google/yamlfmt/formatters/basic"
+)
+
+func TestNewWithConfigRetainsDefaultValues(t *testing.T) {
+	testCases := []struct {
+		name           string
+		configMap      map[string]interface{}
+		expectedConfig basic.Config
+	}{
+		{
+			name: "only indent specified",
+			configMap: map[string]interface{}{
+				"indent": 4,
+			},
+			expectedConfig: basic.Config{
+				Indent:               4,
+				IncludeDocumentStart: false,
+			},
+		},
+		{
+			name: "only include_document_start specified",
+			configMap: map[string]interface{}{
+				"include_document_start": true,
+			},
+			expectedConfig: basic.Config{
+				Indent:               2,
+				IncludeDocumentStart: true,
+			},
+		},
+	}
+
+	factory := basic.BasicFormatterFactory{}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			formatter, err := factory.NewWithConfig(tc.configMap)
+			if err != nil {
+				t.Fatalf("expected factory to create config, got error: %v", err)
+			}
+			basicFormatter, ok := formatter.(*basic.BasicFormatter)
+			if !ok {
+				t.Fatal("should have been able to cast to basic formatter")
+			}
+			if *basicFormatter.Config != tc.expectedConfig {
+				t.Fatalf("configs differed:\nexpected: %v\ngot: %v", *basicFormatter.Config, tc.expectedConfig)
+			}
+		})
+	}
+}