github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/go/spec/absolute_path.go (about)

     1  // Copyright 2016 Attic Labs, Inc. All rights reserved.
     2  // Licensed under the Apache License, version 2.0:
     3  // http://www.apache.org/licenses/LICENSE-2.0
     4  
     5  package spec
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"regexp"
    11  
    12  	"github.com/attic-labs/noms/go/datas"
    13  	"github.com/attic-labs/noms/go/hash"
    14  	"github.com/attic-labs/noms/go/types"
    15  )
    16  
    17  var datasetCapturePrefixRe = regexp.MustCompile("^(" + datas.DatasetRe.String() + ")")
    18  
    19  // AbsolutePath describes the location of a Value within a Noms database.
    20  //
    21  // To locate a value relative to some other value, see Path. To locate a value
    22  // globally, see Spec.
    23  //
    24  // For more on paths, absolute paths, and specs, see:
    25  // https://github.com/attic-labs/noms/blob/master/doc/spelling.md.
    26  type AbsolutePath struct {
    27  	// Dataset is the dataset this AbsolutePath is rooted at. Only one of
    28  	// Dataset and Hash should be set.
    29  	Dataset string
    30  	// Hash is the hash this AbsolutePath is rooted at. Only one of Dataset and
    31  	// Hash should be set.
    32  	Hash hash.Hash
    33  	// Path is the relative path from Dataset or Hash. This can be empty. In
    34  	// that case, the AbsolutePath describes the value at either Dataset or
    35  	// Hash.
    36  	Path types.Path
    37  }
    38  
    39  // NewAbsolutePath attempts to parse 'str' and return an AbsolutePath.
    40  func NewAbsolutePath(str string) (AbsolutePath, error) {
    41  	if len(str) == 0 {
    42  		return AbsolutePath{}, errors.New("Empty path")
    43  	}
    44  
    45  	var h hash.Hash
    46  	var dataset string
    47  	var pathStr string
    48  
    49  	if str[0] == '#' {
    50  		tail := str[1:]
    51  		if len(tail) < hash.StringLen {
    52  			return AbsolutePath{}, errors.New("Invalid hash: " + tail)
    53  		}
    54  
    55  		hashStr := tail[:hash.StringLen]
    56  		if h2, ok := hash.MaybeParse(hashStr); ok {
    57  			h = h2
    58  		} else {
    59  			return AbsolutePath{}, errors.New("Invalid hash: " + hashStr)
    60  		}
    61  
    62  		pathStr = tail[hash.StringLen:]
    63  	} else {
    64  		datasetParts := datasetCapturePrefixRe.FindStringSubmatch(str)
    65  		if datasetParts == nil {
    66  			return AbsolutePath{}, fmt.Errorf("Invalid dataset name: %s", str)
    67  		}
    68  
    69  		dataset = datasetParts[1]
    70  		pathStr = str[len(dataset):]
    71  	}
    72  
    73  	if len(pathStr) == 0 {
    74  		return AbsolutePath{Hash: h, Dataset: dataset}, nil
    75  	}
    76  
    77  	path, err := types.ParsePath(pathStr)
    78  	if err != nil {
    79  		return AbsolutePath{}, err
    80  	}
    81  
    82  	return AbsolutePath{Hash: h, Dataset: dataset, Path: path}, nil
    83  }
    84  
    85  // Resolve returns the Value reachable by 'p' in 'db'.
    86  func (p AbsolutePath) Resolve(db datas.Database) (val types.Value) {
    87  	if len(p.Dataset) > 0 {
    88  		var ok bool
    89  		ds := db.GetDataset(p.Dataset)
    90  		if val, ok = ds.MaybeHead(); !ok {
    91  			val = nil
    92  		}
    93  	} else if !p.Hash.IsEmpty() {
    94  		val = db.ReadValue(p.Hash)
    95  	} else {
    96  		panic("Unreachable")
    97  	}
    98  
    99  	if val != nil && p.Path != nil {
   100  		val = p.Path.Resolve(val, db)
   101  	}
   102  	return
   103  }
   104  
   105  func (p AbsolutePath) IsEmpty() bool {
   106  	return p.Dataset == "" && p.Hash.IsEmpty()
   107  }
   108  
   109  func (p AbsolutePath) String() (str string) {
   110  	if p.IsEmpty() {
   111  		return ""
   112  	}
   113  
   114  	if len(p.Dataset) > 0 {
   115  		str = p.Dataset
   116  	} else if !p.Hash.IsEmpty() {
   117  		str = "#" + p.Hash.String()
   118  	} else {
   119  		panic("Unreachable")
   120  	}
   121  
   122  	return str + p.Path.String()
   123  }
   124  
   125  // ReadAbsolutePaths attempts to parse each path in 'paths' and resolve them.
   126  // If any path fails to parse correctly or if any path can be resolved to an
   127  // existing Noms Value, then this function returns (nil, error).
   128  func ReadAbsolutePaths(db datas.Database, paths ...string) ([]types.Value, error) {
   129  	r := make([]types.Value, 0, len(paths))
   130  	for _, ps := range paths {
   131  		p, err := NewAbsolutePath(ps)
   132  		if err != nil {
   133  			return nil, fmt.Errorf("Invalid input path '%s'", ps)
   134  		}
   135  
   136  		v := p.Resolve(db)
   137  		if v == nil {
   138  			return nil, fmt.Errorf("Input path '%s' does not exist in database", ps)
   139  		}
   140  
   141  		r = append(r, v)
   142  	}
   143  	return r, nil
   144  }