github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/config/resolver.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 config 23 24 import ( 25 "context" 26 "fmt" 27 "strings" 28 29 "github.com/dolthub/dolt/go/store/chunks" 30 "github.com/dolthub/dolt/go/store/datas" 31 "github.com/dolthub/dolt/go/store/prolly/tree" 32 "github.com/dolthub/dolt/go/store/spec" 33 "github.com/dolthub/dolt/go/store/types" 34 "github.com/dolthub/dolt/go/store/util/verbose" 35 ) 36 37 type Resolver struct { 38 config *Config 39 dotDatapath string // set to the first datapath that was resolved 40 } 41 42 // A Resolver enables using db defaults, db aliases and dataset '.' replacement in command 43 // line arguments when a .nomsconfig file is present. To use it, create a config resolver 44 // before command line processing and use it to resolve each dataspec argument in 45 // succession. 46 func NewResolver() *Resolver { 47 c, err := FindNomsConfig() 48 if err != nil { 49 if err != ErrNoConfig { 50 panic(fmt.Errorf("failed to read .nomsconfig due to: %v", err)) 51 } 52 return &Resolver{} 53 } 54 return &Resolver{c, ""} 55 } 56 57 // Print replacement if one occurred 58 func (r *Resolver) verbose(ctx context.Context, orig string, replacement string) string { 59 if orig != replacement { 60 if orig == "" { 61 orig = `""` 62 } 63 verbose.Logger(ctx).Sugar().Debugf("\tresolving %s -> %s", orig, replacement) 64 } 65 return replacement 66 } 67 68 // Resolve string to database name. And get the DbConfig if one exists. 69 // If config is defined: 70 // - replace the empty string with the default db url 71 // - replace any db alias with it's url 72 func (r *Resolver) DbConfigForDbSpec(str string) *DbConfig { 73 if r.config != nil { 74 if str == "" { 75 dbc := r.config.Db[DefaultDbAlias] 76 return &dbc 77 } 78 if val, ok := r.config.Db[str]; ok { 79 return &val 80 } 81 } 82 return &DbConfig{Url: str} 83 } 84 85 // See config.DbConfigForDbSpec which will retrieve the entire DbConfig 86 // object associated with the db (if one exists). This method retrieves 87 // the Url from that DbConfig. 88 func (r *Resolver) ResolveDbSpec(str string) string { 89 return r.DbConfigForDbSpec(str).Url 90 } 91 92 // Resolve string to dataset or path name and get the appropriate 93 // DbConfig if one exists. 94 // - replace database name as described in ResolveDatabase 95 // - if this is the first call to ResolvePath, remember the 96 // datapath part for subsequent calls. 97 // - if this is not the first call and a "." is used, replace 98 // it with the first datapath. 99 func (r *Resolver) ResolvePathSpecAndGetDbConfig(str string) (string, *DbConfig) { 100 iOfSep := strings.Index(str, spec.Separator) 101 if r.config != nil && iOfSep > -1 { 102 split := strings.SplitN(str, spec.Separator, 2) 103 db, rest := "", split[0] 104 if len(split) > 1 { 105 db, rest = split[0], split[1] 106 } 107 108 dbc := r.DbConfigForDbSpec(db) 109 if dbc.Url != "" { 110 if r.dotDatapath == "" { 111 r.dotDatapath = rest 112 } else if rest == "." { 113 rest = r.dotDatapath 114 } 115 116 return dbc.Url + spec.Separator + rest, dbc 117 } 118 } 119 120 return str, &DbConfig{Url: str} 121 } 122 123 // See ResolvePathSpecAndGetDbConfig which does both path spec resolutioon 124 // and config retrieval 125 func (r *Resolver) ResolvePathSpec(str string) string { 126 str, _ = r.ResolvePathSpecAndGetDbConfig(str) 127 128 return str 129 } 130 131 // Resolve string to database spec. If a config is present, 132 // - resolve a db alias to its db spec 133 // - resolve "" to the default db spec 134 func (r *Resolver) GetDatabase(ctx context.Context, str string) (datas.Database, types.ValueReadWriter, tree.NodeStore, error) { 135 dbc := r.DbConfigForDbSpec(str) 136 sp, err := spec.ForDatabaseOpts(r.verbose(ctx, str, dbc.Url), specOptsForConfig(r.config, dbc)) 137 if err != nil { 138 return nil, nil, nil, err 139 } 140 return sp.GetDatabase(ctx), sp.GetVRW(ctx), sp.GetNodeStore(ctx), nil 141 } 142 143 // Resolve string to a chunkstore. Like ResolveDatabase, but returns the underlying ChunkStore 144 func (r *Resolver) GetChunkStore(ctx context.Context, str string) (chunks.ChunkStore, error) { 145 dbc := r.DbConfigForDbSpec(str) 146 sp, err := spec.ForDatabaseOpts(r.verbose(ctx, str, dbc.Url), specOptsForConfig(r.config, dbc)) 147 if err != nil { 148 return nil, err 149 } 150 return sp.NewChunkStore(ctx), nil 151 } 152 153 // Resolve string to a dataset. If a config is present, 154 // - if no db prefix is present, assume the default db 155 // - if the db prefix is an alias, replace it 156 func (r *Resolver) GetDataset(ctx context.Context, str string) (datas.Database, types.ValueReadWriter, datas.Dataset, error) { 157 specStr, dbc := r.ResolvePathSpecAndGetDbConfig(str) 158 sp, err := spec.ForDatasetOpts(r.verbose(ctx, str, specStr), specOptsForConfig(r.config, dbc)) 159 if err != nil { 160 return nil, nil, datas.Dataset{}, err 161 } 162 return sp.GetDatabase(ctx), sp.GetVRW(ctx), sp.GetDataset(ctx), nil 163 } 164 165 // Resolve string to a value path. If a config is present, 166 // - if no db spec is present, assume the default db 167 // - if the db spec is an alias, replace it 168 func (r *Resolver) GetPath(ctx context.Context, str string) (datas.Database, types.ValueReadWriter, types.Value, error) { 169 specStr, dbc := r.ResolvePathSpecAndGetDbConfig(str) 170 sp, err := spec.ForPathOpts(r.verbose(ctx, str, specStr), specOptsForConfig(r.config, dbc)) 171 if err != nil { 172 return nil, nil, nil, err 173 } 174 value, err := sp.GetValue(ctx) 175 if err != nil { 176 return nil, nil, nil, err 177 } 178 179 return sp.GetDatabase(ctx), sp.GetVRW(ctx), value, nil 180 } 181 182 // GetDatabaseSpecForPath returns the database and a VRW for the path given, but does not attempt to load a value 183 func (r *Resolver) GetDatabaseSpecForPath(ctx context.Context, str string) (spec.Spec, error) { 184 specStr, dbc := r.ResolvePathSpecAndGetDbConfig(str) 185 return spec.ForPathOpts(r.verbose(ctx, str, specStr), specOptsForConfig(r.config, dbc)) 186 }