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 }