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  }