github.com/tomwright/dasel@v1.27.3/internal/command/select.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/spf13/cobra"
     6  	"github.com/tomwright/dasel"
     7  	"github.com/tomwright/dasel/storage"
     8  	"io"
     9  	"reflect"
    10  )
    11  
    12  type selectOptions struct {
    13  	File                string
    14  	Parser              string
    15  	ReadParser          string
    16  	WriteParser         string
    17  	Selector            string
    18  	Reader              io.Reader
    19  	Writer              io.Writer
    20  	Multi               bool
    21  	NullValueNotFound   bool
    22  	Compact             bool
    23  	DisplayLength       bool
    24  	MergeInputDocuments bool
    25  	FormatTemplate      string
    26  	Colourise           bool
    27  	EscapeHTML          bool
    28  }
    29  
    30  func outputNodeLength(writer io.Writer, nodes ...*dasel.Node) error {
    31  	for _, n := range nodes {
    32  		val := n.Value
    33  		if val.Kind() == reflect.Interface {
    34  			val = val.Elem()
    35  		}
    36  		length := 0
    37  		switch val.Kind() {
    38  		case reflect.Map, reflect.Slice, reflect.String:
    39  			length = val.Len()
    40  		default:
    41  			length = len(fmt.Sprint(val.Interface()))
    42  		}
    43  		if _, err := fmt.Fprintf(writer, "%d\n", length); err != nil {
    44  			return err
    45  		}
    46  	}
    47  	return nil
    48  }
    49  
    50  func runSelectMultiCommand(cmd *cobra.Command, rootNode *dasel.Node, opts selectOptions, writeParser storage.WriteParser, writeOptions []storage.ReadWriteOption) error {
    51  	var results []*dasel.Node
    52  	var err error
    53  	if opts.Selector == "." {
    54  		results = []*dasel.Node{rootNode}
    55  	} else {
    56  		results, err = rootNode.QueryMultiple(opts.Selector)
    57  	}
    58  
    59  	written, err := customErrorHandling(customErrorHandlingOpts{
    60  		File:     opts.File,
    61  		Writer:   opts.Writer,
    62  		Err:      err,
    63  		Cmd:      cmd,
    64  		NullFlag: opts.NullValueNotFound,
    65  	})
    66  	if err != nil {
    67  		return fmt.Errorf("could not query multiple node: %w", err)
    68  	}
    69  	if written {
    70  		return nil
    71  	}
    72  
    73  	if opts.DisplayLength {
    74  		return outputNodeLength(opts.Writer, results...)
    75  	}
    76  
    77  	if err := writeNodesToOutput(writeNodesToOutputOpts{
    78  		Nodes:          results,
    79  		Parser:         writeParser,
    80  		Writer:         opts.Writer,
    81  		FormatTemplate: opts.FormatTemplate,
    82  	}, cmd, writeOptions...); err != nil {
    83  		return fmt.Errorf("could not write output: %w", err)
    84  	}
    85  	return nil
    86  }
    87  
    88  func runSelectCommand(opts selectOptions, cmd *cobra.Command) error {
    89  	readParser, err := getReadParser(opts.File, opts.ReadParser, opts.Parser)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	rootNode, err := getRootNode(getRootNodeOpts{
    94  		File:                opts.File,
    95  		Parser:              readParser,
    96  		Reader:              opts.Reader,
    97  		MergeInputDocuments: opts.MergeInputDocuments,
    98  	}, cmd)
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	if !rootNode.Value.IsValid() {
   104  		rootNode = dasel.New(&storage.BasicSingleDocument{
   105  			Value: map[string]interface{}{},
   106  		})
   107  	}
   108  
   109  	if opts.Writer == nil {
   110  		opts.Writer = cmd.OutOrStdout()
   111  	}
   112  
   113  	writeParser, err := getWriteParser(readParser, opts.WriteParser, opts.Parser, "-", opts.File, opts.FormatTemplate)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	writeOptions := []storage.ReadWriteOption{
   119  		storage.EscapeHTMLOption(opts.EscapeHTML),
   120  	}
   121  
   122  	if opts.Compact {
   123  		writeOptions = append(writeOptions, storage.PrettyPrintOption(false))
   124  	}
   125  	if opts.Colourise {
   126  		writeOptions = append(writeOptions, storage.ColouriseOption(true))
   127  	}
   128  
   129  	if opts.Multi {
   130  		return runSelectMultiCommand(cmd, rootNode, opts, writeParser, writeOptions)
   131  	}
   132  
   133  	var res *dasel.Node
   134  
   135  	if opts.Selector == "." {
   136  		res = rootNode
   137  	} else {
   138  		res, err = rootNode.Query(opts.Selector)
   139  		if err != nil {
   140  			err = fmt.Errorf("could not query node: %w", err)
   141  		}
   142  	}
   143  
   144  	written, err := customErrorHandling(customErrorHandlingOpts{
   145  		File:     opts.File,
   146  		Writer:   opts.Writer,
   147  		Err:      err,
   148  		Cmd:      cmd,
   149  		NullFlag: opts.NullValueNotFound,
   150  	})
   151  	if err != nil {
   152  		return err
   153  	}
   154  	if written {
   155  		return nil
   156  	}
   157  
   158  	if opts.DisplayLength {
   159  		return outputNodeLength(opts.Writer, res)
   160  	}
   161  
   162  	if err := writeNodeToOutput(writeNodeToOutputOpts{
   163  		Node:           res,
   164  		Parser:         writeParser,
   165  		Writer:         opts.Writer,
   166  		FormatTemplate: opts.FormatTemplate,
   167  	}, cmd, writeOptions...); err != nil {
   168  		return fmt.Errorf("could not write output: %w", err)
   169  	}
   170  
   171  	return nil
   172  }
   173  
   174  func selectCommand() *cobra.Command {
   175  	var fileFlag, selectorFlag, parserFlag, readParserFlag, writeParserFlag, formatTemplateFlag string
   176  	var plainFlag, multiFlag, nullValueNotFoundFlag, compactFlag, lengthFlag, mergeInputDocumentsFlag,
   177  		colourFlag, colorFlag, escapeHTMLFlag bool
   178  
   179  	cmd := &cobra.Command{
   180  		Use:   "select -f <file> -p <json,yaml,toml,xml,csv> <selector>",
   181  		Short: "Select properties from the given file.",
   182  		RunE: func(cmd *cobra.Command, args []string) error {
   183  			if selectorFlag == "" && len(args) > 0 {
   184  				selectorFlag = args[0]
   185  				args = args[1:]
   186  			}
   187  			if plainFlag {
   188  				writeParserFlag = "-"
   189  			}
   190  			return runSelectCommand(selectOptions{
   191  				File:                fileFlag,
   192  				Parser:              parserFlag,
   193  				ReadParser:          readParserFlag,
   194  				WriteParser:         writeParserFlag,
   195  				Selector:            selectorFlag,
   196  				Multi:               multiFlag,
   197  				NullValueNotFound:   nullValueNotFoundFlag,
   198  				Compact:             compactFlag,
   199  				DisplayLength:       lengthFlag,
   200  				MergeInputDocuments: mergeInputDocumentsFlag,
   201  				FormatTemplate:      formatTemplateFlag,
   202  				Colourise:           colourFlag || colorFlag,
   203  				EscapeHTML:          escapeHTMLFlag,
   204  			}, cmd)
   205  		},
   206  	}
   207  
   208  	cmd.Flags().StringVarP(&fileFlag, "file", "f", "", "The file to query.")
   209  	cmd.Flags().StringVarP(&selectorFlag, "selector", "s", "", "The selector to use when querying the data structure.")
   210  	cmd.Flags().StringVarP(&parserFlag, "parser", "p", "", "Shorthand for -r FORMAT -w FORMAT.")
   211  	cmd.Flags().StringVarP(&readParserFlag, "read", "r", "", "The parser to use when reading.")
   212  	cmd.Flags().StringVarP(&writeParserFlag, "write", "w", "", "The parser to use when writing.")
   213  	cmd.Flags().BoolVar(&plainFlag, "plain", false, "Alias of -w plain")
   214  	cmd.Flags().BoolVarP(&multiFlag, "multiple", "m", false, "Select multiple results.")
   215  	cmd.Flags().BoolVarP(&nullValueNotFoundFlag, "null", "n", false, "Output null instead of value not found errors.")
   216  	cmd.Flags().BoolVar(&lengthFlag, "length", false, "Output the length of the selected value.")
   217  	cmd.Flags().BoolVar(&mergeInputDocumentsFlag, "merge-input-documents", false, "Merge multiple input documents into an array.")
   218  	cmd.Flags().BoolVarP(&compactFlag, "compact", "c", false, "Compact the output by removing all pretty-printing where possible.")
   219  	cmd.Flags().StringVar(&formatTemplateFlag, "format", "", "Formatting template to use when writing results.")
   220  	cmd.Flags().BoolVar(&colourFlag, "colour", false, "Print colourised output.")
   221  	cmd.Flags().BoolVar(&colorFlag, "color", false, "Alias of --colour.")
   222  	cmd.Flags().BoolVar(&escapeHTMLFlag, "escape-html", false, "Escape HTML tags when writing output.")
   223  
   224  	_ = cmd.MarkFlagFilename("file")
   225  
   226  	return cmd
   227  }