github.com/tomwright/dasel@v1.27.3/node.go (about)

     1  package dasel
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"reflect"
     8  	"regexp"
     9  
    10  	"github.com/tomwright/dasel/storage"
    11  )
    12  
    13  // Selector represents the selector for a node.
    14  type Selector struct {
    15  	// Raw is the full selector.
    16  	Raw string `json:"raw"`
    17  	// Current is the selector to be used with the current node.
    18  	Current string `json:"current"`
    19  	// Remaining is the remaining parts of the Raw selector.
    20  	Remaining string `json:"remaining"`
    21  	// Type is the type of the selector.
    22  	Type string `json:"type"`
    23  	// Property is the name of the property this selector targets, if applicable.
    24  	Property string `json:"property,omitempty"`
    25  	// Index is the index to use if applicable.
    26  	Index int `json:"index,omitempty"`
    27  	// Conditions contains a set of conditions to optionally match a target.
    28  	Conditions []Condition `json:"conditions,omitempty"`
    29  }
    30  
    31  // Copy returns a copy of the selector.
    32  func (s Selector) Copy() Selector {
    33  	return Selector{
    34  		Raw:        s.Raw,
    35  		Current:    s.Current,
    36  		Remaining:  s.Remaining,
    37  		Type:       s.Type,
    38  		Property:   s.Property,
    39  		Index:      s.Index,
    40  		Conditions: s.Conditions,
    41  	}
    42  }
    43  
    44  // Node represents a single node in the chain of nodes for a selector.
    45  type Node struct {
    46  	// Previous is the previous node in the chain.
    47  	Previous *Node `json:"-"`
    48  	// Next contains the next node in the chain.
    49  	// This is used with Query and Put requests.
    50  	Next *Node `json:"next,omitempty"`
    51  	// NextMultiple contains the next nodes in the chain.
    52  	// This is used with QueryMultiple and PutMultiple requests.
    53  	// When a major version change occurs this will completely replace Next.
    54  	NextMultiple []*Node `json:"nextMultiple,omitempty"`
    55  	// OriginalValue is the value returned from the parser.
    56  	// In most cases this is the same as Value, but is different for thr YAML parser
    57  	// as it contains information on the original document.
    58  	OriginalValue interface{} `json:"-"`
    59  	// Value is the value of the current node.
    60  	Value reflect.Value `json:"value"`
    61  	// Selector is the selector for the current node.
    62  	Selector       Selector `json:"selector"`
    63  	wasInitialised bool
    64  }
    65  
    66  // String returns the value of the node as a string.
    67  // No formatting is done here, you get the raw value.
    68  func (n *Node) String() string {
    69  	return fmt.Sprint(n.InterfaceValue())
    70  }
    71  
    72  // InterfaceValue returns the value stored within the node as an interface{}.
    73  func (n *Node) InterfaceValue() interface{} {
    74  	// We shouldn't be able to get here but this will stop a panic if we do.
    75  	if !n.Value.IsValid() {
    76  		return nil
    77  	}
    78  	return n.Value.Interface()
    79  }
    80  
    81  const (
    82  	propertySelector = `(?P<property>[a-zA-Z\-_]+)`
    83  	indexSelector    = `\[(?P<index>[0-9a-zA-Z\*]*?)\]`
    84  )
    85  
    86  var (
    87  	propertyRegexp   = regexp.MustCompile(fmt.Sprintf("^\\.?%s", propertySelector))
    88  	indexRegexp      = regexp.MustCompile(fmt.Sprintf("^\\.?%s", indexSelector))
    89  	newDynamicRegexp = regexp.MustCompile(fmt.Sprintf("^\\.?((?:\\(.*\\))+)"))
    90  )
    91  
    92  func isValid(value reflect.Value) bool {
    93  	return value.IsValid() && !safeIsNil(value)
    94  }
    95  
    96  func safeIsNil(value reflect.Value) bool {
    97  	switch value.Kind() {
    98  	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer,
    99  		reflect.Interface, reflect.Slice:
   100  		return value.IsNil()
   101  	}
   102  	return false
   103  }
   104  
   105  func nilValue() reflect.Value {
   106  	return reflect.ValueOf(nil)
   107  }
   108  
   109  func unwrapValue(value reflect.Value) reflect.Value {
   110  	if value.Kind() == reflect.Interface && !value.IsNil() {
   111  		return value.Elem()
   112  	}
   113  	return value
   114  }
   115  
   116  func derefValue(value reflect.Value) reflect.Value {
   117  	if value.Kind() == reflect.Ptr {
   118  		return value.Elem()
   119  	}
   120  	return value
   121  }
   122  
   123  // New returns a new root node with the given value.
   124  func New(value interface{}) *Node {
   125  	rootNode := &Node{
   126  		Previous:     nil,
   127  		Next:         nil,
   128  		NextMultiple: nil,
   129  		Selector: Selector{
   130  			Raw:       ".",
   131  			Current:   ".",
   132  			Remaining: "",
   133  			Type:      "ROOT",
   134  			Property:  "",
   135  		},
   136  	}
   137  	rootNode.setRealValue(value)
   138  	return rootNode
   139  }
   140  
   141  // NewFromFile returns a new root node by parsing file using specified read parser.
   142  func NewFromFile(filename, parser string) (*Node, error) {
   143  	readParser, err := storage.NewReadParserFromString(parser)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	data, err := storage.LoadFromFile(filename, readParser)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	return New(data), nil
   154  }
   155  
   156  // NewFromReader returns a new root node by parsing from Reader using specified read parser.
   157  func NewFromReader(reader io.Reader, parser string) (*Node, error) {
   158  	readParser, err := storage.NewReadParserFromString(parser)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	data, err := storage.Load(readParser, reader)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	return New(data), nil
   169  }
   170  
   171  // WriteToFile writes data to the given file with the specified options.
   172  func (n *Node) WriteToFile(filename, parser string, writeOptions []storage.ReadWriteOption) error {
   173  	f, err := os.Create(filename)
   174  
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	// https://www.joeshaw.org/dont-defer-close-on-writable-files/
   180  	if err = n.Write(f, parser, writeOptions); err != nil {
   181  		_ = f.Close()
   182  		return err
   183  	}
   184  
   185  	return f.Close()
   186  }
   187  
   188  // Write writes data to Writer using specified write parser and options.
   189  func (n *Node) Write(writer io.Writer, parser string, writeOptions []storage.ReadWriteOption) error {
   190  	writeParser, err := storage.NewWriteParserFromString(parser)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	value := n.InterfaceValue()
   196  	originalValue := n.OriginalValue
   197  
   198  	if err := storage.Write(writeParser, value, originalValue, writer, writeOptions...); err != nil {
   199  		return err
   200  	}
   201  
   202  	return nil
   203  }
   204  
   205  func (n *Node) setValue(newValue interface{}) {
   206  	n.Value = reflect.ValueOf(newValue)
   207  	if n.Selector.Type == "ROOT" {
   208  		n.OriginalValue = newValue
   209  	}
   210  }
   211  
   212  func (n *Node) setRealValue(newValue interface{}) {
   213  	switch typed := newValue.(type) {
   214  	case storage.RealValue:
   215  		n.Value = reflect.ValueOf(typed.RealValue())
   216  	default:
   217  		n.Value = reflect.ValueOf(typed)
   218  	}
   219  	if n.Selector.Type == "ROOT" {
   220  		n.OriginalValue = newValue
   221  	}
   222  }
   223  
   224  func (n *Node) setReflectValue(newValue reflect.Value) {
   225  	n.Value = newValue
   226  	if n.Selector.Type == "ROOT" {
   227  		n.OriginalValue = unwrapValue(newValue).Interface()
   228  	}
   229  }
   230  
   231  func (n *Node) setRealReflectValue(newValue reflect.Value) {
   232  	val := unwrapValue(newValue).Interface()
   233  	switch typed := val.(type) {
   234  	case storage.RealValue:
   235  		n.OriginalValue = typed
   236  		n.Value = reflect.ValueOf(typed.RealValue())
   237  	default:
   238  		n.Value = newValue
   239  	}
   240  	if n.Selector.Type == "ROOT" {
   241  		n.OriginalValue = val
   242  	}
   243  }