Repository: TomWright/dasel
Pull request #247 (2022-09-26 12:56:23)

Add --value-file flag


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2253b4..8cd8b5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
-- Nothing yet
+- Nothing yet.
+
+## [v1.27.0] - 2022-09-26
+### Added
+
+- New `value-file` flag allows you to `put` values read from a file ([Issue 246](https://github.com/TomWright/dasel/issues/246))
 
 ## [v1.26.1] - 2022-08-24
 
@@ -476,7 +481,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - Everything!
 
-[unreleased]: https://github.com/TomWright/dasel/compare/v1.26.1...HEAD
+[unreleased]: https://github.com/TomWright/dasel/compare/v1.27.0...HEAD
+[v1.27.0]: https://github.com/TomWright/dasel/compare/v1.26.1...v1.27.0
 [v1.26.1]: https://github.com/TomWright/dasel/compare/v1.26.0...v1.26.1
 [v1.26.0]: https://github.com/TomWright/dasel/compare/v1.25.1...v1.26.0
 [v1.25.1]: https://github.com/TomWright/dasel/compare/v1.25.0...v1.25.1
diff --git a/internal/command/put.go b/internal/command/put.go
index cafa443..c40ec43 100644
--- a/internal/command/put.go
+++ b/internal/command/put.go
@@ -290,7 +290,7 @@ func getOutputWriter(cmd *cobra.Command, in io.Writer, file string, out string)
 }
 
 func putCommand() *cobra.Command {
-	var fileFlag, selectorFlag, parserFlag, readParserFlag, writeParserFlag, outFlag, valueFlag string
+	var fileFlag, selectorFlag, parserFlag, readParserFlag, writeParserFlag, outFlag, valueFlag, valueFileFlag string
 	var multiFlag, compactFlag, mergeInputDocumentsFlag, escapeHTMLFlag bool
 
 	cmd := &cobra.Command{
@@ -316,6 +316,7 @@ func putCommand() *cobra.Command {
 	cmd.PersistentFlags().BoolVarP(&compactFlag, "compact", "c", false, "Compact the output by removing all pretty-printing where possible.")
 	cmd.PersistentFlags().BoolVar(&mergeInputDocumentsFlag, "merge-input-documents", false, "Merge multiple input documents into an array.")
 	cmd.PersistentFlags().StringVarP(&valueFlag, "value", "v", "", "Value to put.")
+	cmd.PersistentFlags().StringVar(&valueFileFlag, "value-file", "", "File containing value to put.")
 	cmd.PersistentFlags().BoolVar(&escapeHTMLFlag, "escape-html", false, "Escape HTML tags when writing output.")
 
 	_ = cmd.MarkPersistentFlagFilename("file")
@@ -331,6 +332,7 @@ type genericPutOptions struct {
 	WriteParser         string
 	Selector            string
 	Value               string
+	ValueFile           string
 	ValueType           string
 	Init                func(genericPutOptions) genericPutOptions
 	Reader              io.Reader
@@ -353,6 +355,7 @@ func getGenericInit(cmd *cobra.Command, args []string) func(options genericPutOp
 		opts.Compact, _ = cmd.Flags().GetBool("compact")
 		opts.MergeInputDocuments, _ = cmd.Flags().GetBool("merge-input-documents")
 		opts.Value, _ = cmd.Flags().GetString("value")
+		opts.ValueFile, _ = cmd.Flags().GetString("value-file")
 		opts.EscapeHTML, _ = cmd.Flags().GetBool("escape-html")
 
 		if opts.Selector == "" && len(args) > 0 {
@@ -360,7 +363,7 @@ func getGenericInit(cmd *cobra.Command, args []string) func(options genericPutOp
 			args = args[1:]
 		}
 
-		if opts.Value == "" && len(args) > 0 {
+		if opts.Value == "" && opts.ValueFile == "" && len(args) > 0 {
 			opts.Value = args[0]
 			args = args[1:]
 		}
@@ -387,6 +390,14 @@ func runGenericPutCommand(opts genericPutOptions, cmd *cobra.Command) error {
 		return err
 	}
 
+	if opts.ValueFile != "" {
+		valueFile, err := readFileContents(opts.ValueFile)
+		if err != nil {
+			return err
+		}
+		opts.Value = string(valueFile)
+	}
+
 	updateValue, err := parseValue(opts.Value, opts.ValueType)
 	if err != nil {
 		return err
diff --git a/internal/command/put_document_internal.go b/internal/command/put_document_internal.go
index a5d9119..2d03d18 100644
--- a/internal/command/put_document_internal.go
+++ b/internal/command/put_document_internal.go
@@ -5,6 +5,7 @@ import (
 	"github.com/spf13/cobra"
 	"github.com/tomwright/dasel/storage"
 	"io"
+	"os"
 )
 
 type putDocumentOpts struct {
@@ -14,6 +15,7 @@ type putDocumentOpts struct {
 	WriteParser         string
 	Parser              string
 	Selector            string
+	DocumentFile        string
 	DocumentString      string
 	DocumentParser      string
 	Reader              io.Reader
@@ -24,6 +26,21 @@ type putDocumentOpts struct {
 	EscapeHTML          bool
 }
 
+func readFileContents(path string) ([]byte, error) {
+	docFile, err := os.Open(path)
+	if err != nil {
+		return nil, fmt.Errorf("could not open file: %s: %w", path, err)
+	}
+	defer docFile.Close()
+
+	bytes, err := io.ReadAll(docFile)
+	if err != nil {
+		return nil, fmt.Errorf("could not read file: %s: %w", path, err)
+	}
+
+	return bytes, nil
+}
+
 func runPutDocumentCommand(opts putDocumentOpts, cmd *cobra.Command) error {
 	readParser, err := getReadParser(opts.File, opts.ReadParser, opts.Parser)
 	if err != nil {
@@ -44,6 +61,14 @@ func runPutDocumentCommand(opts putDocumentOpts, cmd *cobra.Command) error {
 		return err
 	}
 
+	if opts.DocumentFile != "" {
+		docFile, err := readFileContents(opts.DocumentFile)
+		if err != nil {
+			return err
+		}
+		opts.DocumentString = string(docFile)
+	}
+
 	documentValue, err := documentParser.FromBytes([]byte(opts.DocumentString))
 	if err != nil {
 		return fmt.Errorf("could not parse document: %w", err)
@@ -105,6 +130,7 @@ func putDocumentCommand() *cobra.Command {
 			opts.Multi, _ = cmd.Flags().GetBool("multiple")
 			opts.Compact, _ = cmd.Flags().GetBool("compact")
 			opts.DocumentString, _ = cmd.Flags().GetString("value")
+			opts.DocumentFile, _ = cmd.Flags().GetString("value-file")
 			opts.MergeInputDocuments, _ = cmd.Flags().GetBool("merge-input-documents")
 			opts.EscapeHTML, _ = cmd.Flags().GetBool("escape-html")
 
@@ -113,7 +139,7 @@ func putDocumentCommand() *cobra.Command {
 				args = args[1:]
 			}
 
-			if opts.DocumentString == "" && len(args) > 0 {
+			if opts.DocumentString == "" && opts.DocumentFile == "" && len(args) > 0 {
 				opts.DocumentString = args[0]
 				args = args[1:]
 			}


Test patch:
diff --git a/internal/command/root_put_test.go b/internal/command/root_put_test.go
index 75967f8..06eb1d7 100644
--- a/internal/command/root_put_test.go
+++ b/internal/command/root_put_test.go
@@ -736,6 +736,32 @@ foo: true
   "user": "Tom <contact@tomwright.me>"
 }
 `, nil, "--escape-html=false"))
+
+	t.Run("StringFromFile", putStringTest(`{
+  "id": "x"
+}`, "json", ".value", "", `{
+  "id": "x",
+  "value": "This is a string value"
+}
+`, nil, "--value-file", "../../tests/assets/string-value.txt"))
+
+	t.Run("IntFromFile", putIntTest(`{
+  "id": "x"
+}`, "json", ".value", "", `{
+  "id": "x",
+  "value": 12345
+}
+`, nil, "--value-file", "../../tests/assets/int-value.txt"))
+
+	t.Run("DocumentFromFile", putDocumentTest(`{
+  "id": "x"
+}`, "json", ".value", "", `{
+  "id": "x",
+  "value": {
+    "this": "is a value"
+  }
+}
+`, nil, "--value-file", "../../tests/assets/json-value.json"))
 }
 
 func TestRootCMD_Put_YAML(t *testing.T) {
diff --git a/internal/command/root_validate_test.go b/internal/command/root_validate_test.go
index 0ddc4b8..33cd96b 100644
--- a/internal/command/root_validate_test.go
+++ b/internal/command/root_validate_test.go
@@ -28,6 +28,7 @@ fail ./../../tests/assets/broken.xml could not load input: could not unmarshal d
 		"1 files failed validation",
 		`fail ../../tests/assets/broken.json could not load input: could not unmarshal data: invalid character '}' after array element
 pass ../../tests/assets/example.json
+pass ../../tests/assets/json-value.json
 `,
 	))
 
diff --git a/tests/assets/int-value.txt b/tests/assets/int-value.txt
new file mode 100644
index 0000000..bd41cba
--- /dev/null
+++ b/tests/assets/int-value.txt
@@ -0,0 +1,1 @@
+12345
\ No newline at end of file
diff --git a/tests/assets/json-value.json b/tests/assets/json-value.json
new file mode 100644
index 0000000..8963489
--- /dev/null
+++ b/tests/assets/json-value.json
@@ -0,0 +1,3 @@
+{
+    "this": "is a value"
+}
\ No newline at end of file
diff --git a/tests/assets/string-value.txt b/tests/assets/string-value.txt
new file mode 100644
index 0000000..8b94090
--- /dev/null
+++ b/tests/assets/string-value.txt
@@ -0,0 +1,1 @@
+This is a string value
\ No newline at end of file