github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/types/path.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  package types
    23  
    24  import (
    25  	"bytes"
    26  	"context"
    27  	"errors"
    28  	"fmt"
    29  	"math"
    30  	"regexp"
    31  	"strconv"
    32  	"strings"
    33  
    34  	"github.com/dolthub/dolt/go/store/d"
    35  	"github.com/dolthub/dolt/go/store/hash"
    36  )
    37  
    38  // For an annotation like @type, 1st capture group is the annotation.
    39  // For @at(42), 1st capture group is the annotation and 3rd is the parameter.
    40  // Note, @at() is valid under this regexp, code should deal with the error.
    41  var annotationRe = regexp.MustCompile(`^([a-z]+)(\(([\w\-"']*)\))?`)
    42  
    43  // A Path locates a value in Noms relative to some other value. For locating
    44  // values absolutely within a database, see AbsolutePath. To locate values
    45  // globally, see Spec.
    46  //
    47  // For more details, see:
    48  // https://github.com/attic-labs/noms/blob/master/doc/spelling.md.
    49  type Path []PathPart
    50  
    51  type PathPart interface {
    52  	Resolve(ctx context.Context, v Value, vr ValueReader) (Value, error)
    53  	String() string
    54  }
    55  
    56  // ParsePath parses str into a Path, or returns an error if parsing failed.
    57  func ParsePath(str string) (Path, error) {
    58  	if str == "" {
    59  		return Path{}, errors.New("empty path")
    60  	}
    61  	return constructPath(Path{}, str)
    62  }
    63  
    64  // MustParsePath parses str into a Path, or panics if parsing failed.
    65  func MustParsePath(str string) Path {
    66  	p, err := ParsePath(str)
    67  	if err != nil {
    68  		panic(err)
    69  	}
    70  	return p
    71  }
    72  
    73  type keyIndexable interface {
    74  	setIntoKey(v bool) keyIndexable
    75  }
    76  
    77  func constructPath(p Path, str string) (Path, error) {
    78  	if len(str) == 0 {
    79  		return p, nil
    80  	}
    81  
    82  	op, tail := str[0], str[1:]
    83  
    84  	switch op {
    85  	case '.':
    86  		idx := fieldNameComponentRe.FindIndex([]byte(tail))
    87  		if idx == nil {
    88  			return Path{}, errors.New("invalid field: " + tail)
    89  		}
    90  		p = append(p, FieldPath{tail[:idx[1]]})
    91  		return constructPath(p, tail[idx[1]:])
    92  
    93  	case '[':
    94  		if len(tail) == 0 {
    95  			return Path{}, errors.New("path ends in [")
    96  		}
    97  
    98  		idx, h, rem, err := ParsePathIndex(tail)
    99  		if err != nil {
   100  			return Path{}, err
   101  		}
   102  		if !strings.HasPrefix(rem, "]") {
   103  			return Path{}, errors.New("[ is missing closing ]")
   104  		}
   105  		d.PanicIfTrue(idx == nil && h.IsEmpty())
   106  		d.PanicIfTrue(idx != nil && !h.IsEmpty())
   107  
   108  		if idx != nil {
   109  			p = append(p, NewIndexPath(idx))
   110  		} else {
   111  			p = append(p, NewHashIndexPath(h))
   112  		}
   113  		return constructPath(p, rem[1:])
   114  
   115  	case '@':
   116  		ann, hasArg, arg, rem := getAnnotation(tail)
   117  
   118  		switch ann {
   119  		case "at":
   120  			if arg == "" {
   121  				return Path{}, fmt.Errorf("@at annotation requires a position argument")
   122  			}
   123  			idx, err := strconv.ParseInt(arg, 10, 64)
   124  			if err != nil {
   125  				return Path{}, fmt.Errorf("invalid position: %s", arg)
   126  			}
   127  			return constructPath(append(p, NewAtAnnotation(idx)), rem)
   128  
   129  		case "key":
   130  			if hasArg {
   131  				return Path{}, fmt.Errorf("@key annotation does not support arguments")
   132  			}
   133  			if len(p) == 0 {
   134  				return Path{}, fmt.Errorf("cannot use @key annotation at beginning of path")
   135  			}
   136  			lastPart := p[len(p)-1]
   137  			if ki, ok := lastPart.(keyIndexable); ok {
   138  				p[len(p)-1] = ki.setIntoKey(true).(PathPart)
   139  				return constructPath(p, rem)
   140  			}
   141  			return Path{}, fmt.Errorf("cannot use @key annotation on: %s", lastPart.String())
   142  
   143  		case "target":
   144  			if hasArg {
   145  				return Path{}, fmt.Errorf("@target annotation does not support arguments")
   146  			}
   147  			return constructPath(append(p, TargetAnnotation{}), rem)
   148  
   149  		case "type":
   150  			if hasArg {
   151  				return Path{}, fmt.Errorf("@type annotation does not support arguments")
   152  			}
   153  			return constructPath(append(p, TypeAnnotation{}), rem)
   154  
   155  		default:
   156  			return Path{}, fmt.Errorf("unsupported annotation: @%s", ann)
   157  		}
   158  
   159  	case ']':
   160  		return Path{}, errors.New("] is missing opening [")
   161  
   162  	default:
   163  		return Path{}, fmt.Errorf("invalid operator: %c", op)
   164  	}
   165  }
   166  
   167  // Resolve resolves a path relative to some value.
   168  // A ValueReader is required to resolve paths that contain the @target annotation.
   169  func (p Path) Resolve(ctx context.Context, v Value, vr ValueReader) (Value, error) {
   170  	resolved := v
   171  	for _, part := range p {
   172  		if resolved == nil {
   173  			break
   174  		}
   175  
   176  		var err error
   177  		resolved, err = part.Resolve(ctx, resolved, vr)
   178  
   179  		if err != nil {
   180  			return nil, err
   181  		}
   182  	}
   183  
   184  	return resolved, nil
   185  }
   186  
   187  func (p Path) Equals(o Path) bool {
   188  	if len(p) != len(o) {
   189  		return false
   190  	}
   191  	for i, pp := range p {
   192  		if pp != o[i] {
   193  			return false
   194  		}
   195  	}
   196  	return true
   197  }
   198  
   199  // Append makes a copy of a p and appends the PathPart 'pp' to it.
   200  func (p Path) Append(pp PathPart) Path {
   201  	p1 := make(Path, len(p), len(p)+1)
   202  	copy(p1, p)
   203  	return append(p1, pp)
   204  }
   205  
   206  func (p Path) String() string {
   207  	strs := make([]string, 0, len(p))
   208  	for _, part := range p {
   209  		strs = append(strs, part.String())
   210  	}
   211  	return strings.Join(strs, "")
   212  }
   213  
   214  func (p Path) IsEmpty() bool {
   215  	return len(p) == 0
   216  }
   217  
   218  // FieldPath references Struct field values by name.
   219  type FieldPath struct {
   220  	// The name of the field, e.g. `.Name`.
   221  	Name string
   222  }
   223  
   224  func NewFieldPath(name string) FieldPath {
   225  	return FieldPath{name}
   226  }
   227  
   228  func (fp FieldPath) Resolve(ctx context.Context, v Value, vr ValueReader) (Value, error) {
   229  	switch v := v.(type) {
   230  	case Struct:
   231  		if sv, ok, err := v.MaybeGet(fp.Name); err != nil {
   232  			return nil, err
   233  		} else if ok {
   234  			return sv, nil
   235  		}
   236  	case *Type:
   237  		if desc, ok := v.Desc.(StructDesc); ok {
   238  			if df, _ := desc.Field(fp.Name); df != nil {
   239  				return df, nil
   240  			}
   241  		}
   242  	}
   243  	return nil, nil
   244  }
   245  
   246  func (fp FieldPath) String() string {
   247  	return fmt.Sprintf(".%s", fp.Name)
   248  }
   249  
   250  // IndexPath ndexes into Maps and Lists by key or index.
   251  type IndexPath struct {
   252  	// The value of the index, e.g. `[42]` or `["value"]`. If Index is a negative
   253  	// number and the path is resolved in a List, it means index from the back.
   254  	Index Value
   255  	// Whether this index should resolve to the key of a map, given by a `@key`
   256  	// annotation. Typically IntoKey is false, and indices would resolve to the
   257  	// values. E.g. given `{a: 42}` then `["a"]` resolves to `42`. If IntoKey is
   258  	// true, then it resolves to `"a"`. For IndexPath this isn't particularly
   259  	// useful - it's mostly provided for consistency with HashIndexPath - but
   260  	// note that given `{a: 42}` then `["b"]` resolves to nil, not `"b"`.
   261  	IntoKey bool
   262  }
   263  
   264  func NewIndexPath(idx Value) IndexPath {
   265  	return newIndexPath(idx, false)
   266  }
   267  
   268  func ValueCanBePathIndex(v Value) bool {
   269  	k := v.Kind()
   270  	return k == StringKind || k == BoolKind || k == FloatKind
   271  }
   272  
   273  func newIndexPath(idx Value, intoKey bool) IndexPath {
   274  	d.PanicIfFalse(ValueCanBePathIndex(idx))
   275  	return IndexPath{idx, intoKey}
   276  }
   277  
   278  func (ip IndexPath) Resolve(ctx context.Context, v Value, vr ValueReader) (Value, error) {
   279  	seqIndex := func(getter func(i uint64) (Value, error)) (Value, error) {
   280  		n, ok := ip.Index.(Float)
   281  		if !ok {
   282  			return nil, nil
   283  		}
   284  		f := float64(n)
   285  		if f != math.Trunc(f) {
   286  			return nil, nil
   287  		}
   288  		ai, ok := getAbsoluteIndex(v, int64(f))
   289  		if !ok {
   290  			return nil, nil
   291  		}
   292  		if ip.IntoKey {
   293  			return Float(ai), nil
   294  		}
   295  		return getter(ai)
   296  	}
   297  
   298  	switch v := v.(type) {
   299  	case List:
   300  		return seqIndex(func(i uint64) (Value, error) { return v.Get(ctx, i) })
   301  	case *Type:
   302  		if cd, ok := v.Desc.(CompoundDesc); ok {
   303  			return seqIndex(func(i uint64) (Value, error) { return cd.ElemTypes[i], nil })
   304  		}
   305  	case Map:
   306  		if !ip.IntoKey {
   307  			v, _, err := v.MaybeGet(ctx, ip.Index)
   308  			return v, err
   309  		}
   310  
   311  		if has, err := v.Has(ctx, ip.Index); err != nil {
   312  			return nil, err
   313  		} else if has {
   314  			return ip.Index, nil
   315  		}
   316  	}
   317  
   318  	return nil, nil
   319  }
   320  
   321  func (ip IndexPath) String() (str string) {
   322  	valStr, err := EncodedIndexValue(context.Background(), ip.Index)
   323  
   324  	if err != nil {
   325  		return "error: " + err.Error()
   326  	}
   327  
   328  	str = fmt.Sprintf("[%s]", valStr)
   329  	if ip.IntoKey {
   330  		str += "@key"
   331  	}
   332  	return
   333  }
   334  
   335  func (ip IndexPath) setIntoKey(v bool) keyIndexable {
   336  	ip.IntoKey = v
   337  	return ip
   338  }
   339  
   340  // Indexes into Maps by the hash of a key, or a Set by the hash of a value.
   341  type HashIndexPath struct {
   342  	// The hash of the key or value to search for. Maps and Set are ordered, so
   343  	// this in O(log(size)).
   344  	Hash hash.Hash
   345  	// Whether this index should resolve to the key of a map, given by a `@key`
   346  	// annotation. Typically IntoKey is false, and indices would resolve to the
   347  	// values. E.g. given `{a: 42}` and if the hash of `"a"` is `#abcd`, then
   348  	// `[#abcd]` resolves to `42`. If IntoKey is true, then it resolves to `"a"`.
   349  	// This is useful for when Map keys aren't primitive values, e.g. a struct,
   350  	// since struct literals can't be spelled using a Path.
   351  	IntoKey bool
   352  }
   353  
   354  func NewHashIndexPath(h hash.Hash) HashIndexPath {
   355  	return newHashIndexPath(h, false)
   356  }
   357  
   358  func newHashIndexPath(h hash.Hash, intoKey bool) HashIndexPath {
   359  	d.PanicIfTrue(h.IsEmpty())
   360  	return HashIndexPath{h, intoKey}
   361  }
   362  
   363  func (hip HashIndexPath) Resolve(ctx context.Context, v Value, vr ValueReader) (Value, error) {
   364  	var seq orderedSequence
   365  	var getCurrentValue func(cur *sequenceCursor) (Value, error)
   366  
   367  	switch v := v.(type) {
   368  	case Set:
   369  		// Unclear what the behavior should be if |hip.IntoKey| is true, but ignoring it for sets is arguably correct.
   370  		seq = v.orderedSequence
   371  		getCurrentValue = func(cur *sequenceCursor) (Value, error) {
   372  			item, err := cur.current()
   373  
   374  			if err != nil {
   375  				return nil, err
   376  			}
   377  
   378  			return item.(Value), nil
   379  		}
   380  	case Map:
   381  		seq = v.orderedSequence
   382  		if hip.IntoKey {
   383  			getCurrentValue = func(cur *sequenceCursor) (Value, error) {
   384  				item, err := cur.current()
   385  
   386  				if err != nil {
   387  					return nil, err
   388  				}
   389  
   390  				return item.(mapEntry).key, nil
   391  			}
   392  		} else {
   393  			getCurrentValue = func(cur *sequenceCursor) (Value, error) {
   394  				item, err := cur.current()
   395  
   396  				if err != nil {
   397  					return nil, err
   398  				}
   399  
   400  				return item.(mapEntry).value, nil
   401  			}
   402  		}
   403  	default:
   404  		return nil, nil
   405  	}
   406  
   407  	cur, err := newCursorAt(ctx, seq, orderedKeyFromHash(hip.Hash), false, false)
   408  
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  
   413  	if !cur.valid() {
   414  		return nil, nil
   415  	}
   416  
   417  	currKey, err := getCurrentKey(cur)
   418  
   419  	if err != nil {
   420  		return nil, err
   421  	}
   422  
   423  	if currKey.h != hip.Hash {
   424  		return nil, nil
   425  	}
   426  
   427  	return getCurrentValue(cur)
   428  }
   429  
   430  func (hip HashIndexPath) String() (str string) {
   431  	str = fmt.Sprintf("[#%s]", hip.Hash.String())
   432  	if hip.IntoKey {
   433  		str += "@key"
   434  	}
   435  	return
   436  }
   437  
   438  func (hip HashIndexPath) setIntoKey(v bool) keyIndexable {
   439  	hip.IntoKey = v
   440  	return hip
   441  }
   442  
   443  // Parse a Noms value from the path index syntax.
   444  // 4 ->          types.Float
   445  // "4" ->        types.String
   446  // true|false -> types.Boolean
   447  // #<chars> ->   hash.Hash
   448  func ParsePathIndex(str string) (idx Value, h hash.Hash, rem string, err error) {
   449  Switch:
   450  	switch str[0] {
   451  	case '"':
   452  		// String is complicated because ] might be quoted, and " or \ might be escaped.
   453  		stringBuf := bytes.Buffer{}
   454  		i := 1
   455  
   456  		for ; i < len(str); i++ {
   457  			c := str[i]
   458  			if c == '"' {
   459  				i++
   460  				break
   461  			}
   462  			if c == '\\' && i < len(str)-1 {
   463  				i++
   464  				c = str[i]
   465  				if c != '\\' && c != '"' {
   466  					err = errors.New(`only " and \ can be escaped`)
   467  					break Switch
   468  				}
   469  			}
   470  			stringBuf.WriteByte(c)
   471  		}
   472  
   473  		idx = String(stringBuf.String())
   474  		rem = str[i:]
   475  
   476  	default:
   477  		idxStr := str
   478  		sepIdx := strings.Index(str, "]")
   479  		if sepIdx >= 0 {
   480  			idxStr = str[:sepIdx]
   481  			rem = str[sepIdx:]
   482  		}
   483  		if len(idxStr) == 0 {
   484  			err = errors.New("empty index value")
   485  		} else if idxStr[0] == '#' {
   486  			hashStr := idxStr[1:]
   487  			h, _ = hash.MaybeParse(hashStr)
   488  			if h.IsEmpty() {
   489  				err = errors.New("invalid hash: " + hashStr)
   490  			}
   491  		} else if idxStr == "true" {
   492  			idx = Bool(true)
   493  		} else if idxStr == "false" {
   494  			idx = Bool(false)
   495  		} else if i, err2 := strconv.ParseFloat(idxStr, 64); err2 == nil {
   496  			// Should we be more strict here? ParseFloat allows leading and trailing dots, and exponents.
   497  			idx = Float(i)
   498  		} else {
   499  			err = errors.New("invalid index: " + idxStr)
   500  		}
   501  	}
   502  
   503  	return
   504  }
   505  
   506  // TypeAnnotation is a PathPart annotation to resolve to the type of the value
   507  // it's resolved in.
   508  type TypeAnnotation struct {
   509  }
   510  
   511  func (ann TypeAnnotation) Resolve(ctx context.Context, v Value, vr ValueReader) (Value, error) {
   512  	return TypeOf(v)
   513  }
   514  
   515  func (ann TypeAnnotation) String() string {
   516  	return "@type"
   517  }
   518  
   519  // TargetAnnotation is a PathPart annotation to resolve to the targetValue of the Ref it is resolved on.
   520  type TargetAnnotation struct {
   521  }
   522  
   523  func (ann TargetAnnotation) Resolve(ctx context.Context, v Value, vr ValueReader) (Value, error) {
   524  	if vr == nil {
   525  		d.Panic("@target annotation requires a database to resolve against")
   526  	}
   527  	if r, ok := v.(Ref); ok {
   528  		return r.TargetValue(ctx, vr)
   529  	} else {
   530  		return nil, nil
   531  	}
   532  }
   533  
   534  func (ann TargetAnnotation) String() string {
   535  	return "@target"
   536  }
   537  
   538  // AtAnnotation is a PathPart annotation that gets the value of a collection at
   539  // a position, rather than a key. This is equivalent to IndexPath for lists,
   540  // but different for sets and maps.
   541  type AtAnnotation struct {
   542  	// Index is the position to resolve at. If negative, it means an index
   543  	// relative to the end of the collection.
   544  	Index int64
   545  	// IntoKey see IndexPath.IntoKey.
   546  	IntoKey bool
   547  }
   548  
   549  func NewAtAnnotation(idx int64) AtAnnotation {
   550  	return AtAnnotation{idx, false}
   551  }
   552  
   553  func (ann AtAnnotation) Resolve(ctx context.Context, v Value, vr ValueReader) (Value, error) {
   554  	ai, ok := getAbsoluteIndex(v, ann.Index)
   555  	if !ok {
   556  		return nil, nil
   557  	}
   558  
   559  	switch v := v.(type) {
   560  	case List:
   561  		if !ann.IntoKey {
   562  			return v.Get(ctx, ai)
   563  		}
   564  	case Set:
   565  		return v.At(ctx, ai)
   566  	case Map:
   567  		k, mapv, err := v.At(ctx, ai)
   568  
   569  		if err != nil {
   570  			return nil, err
   571  		}
   572  
   573  		if ann.IntoKey {
   574  			return k, nil
   575  		}
   576  
   577  		return mapv, nil
   578  	case *Type:
   579  		if cd, ok := v.Desc.(CompoundDesc); ok {
   580  			return cd.ElemTypes[ai], nil
   581  		}
   582  	}
   583  
   584  	return nil, nil
   585  }
   586  
   587  func (ann AtAnnotation) String() (str string) {
   588  	str = fmt.Sprintf("@at(%d)", ann.Index)
   589  	if ann.IntoKey {
   590  		str += "@key"
   591  	}
   592  	return
   593  }
   594  
   595  func (ann AtAnnotation) setIntoKey(v bool) keyIndexable {
   596  	ann.IntoKey = v
   597  	return ann
   598  }
   599  
   600  func getAnnotation(str string) (ann string, hasArg bool, arg, rem string) {
   601  	parts := annotationRe.FindStringSubmatch(str)
   602  	if parts == nil {
   603  		return
   604  	}
   605  
   606  	ann = parts[1]
   607  	hasArg = parts[2] != ""
   608  	arg = parts[3]
   609  	rem = str[len(parts[0]):]
   610  	return
   611  }
   612  
   613  func getAbsoluteIndex(v Value, relIdx int64) (absIdx uint64, ok bool) {
   614  	var l uint64
   615  	switch v := v.(type) {
   616  	case Collection:
   617  		l = v.Len()
   618  	case *Type:
   619  		if cd, cdOK := v.Desc.(CompoundDesc); cdOK {
   620  			l = uint64(len(cd.ElemTypes))
   621  		} else {
   622  			return
   623  		}
   624  	default:
   625  		return
   626  	}
   627  
   628  	if relIdx < 0 {
   629  		if uint64(-relIdx) > l {
   630  			return
   631  		}
   632  		absIdx = l - uint64(-relIdx)
   633  	} else {
   634  		if uint64(relIdx) >= l {
   635  			return
   636  		}
   637  		absIdx = uint64(relIdx)
   638  	}
   639  
   640  	ok = true
   641  	return
   642  }