github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/cmd/noms/noms_map.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 2018 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 main
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  
    28  	"github.com/attic-labs/kingpin"
    29  
    30  	"github.com/dolthub/dolt/go/store/cmd/noms/util"
    31  	"github.com/dolthub/dolt/go/store/d"
    32  	"github.com/dolthub/dolt/go/store/datas"
    33  	"github.com/dolthub/dolt/go/store/diff"
    34  	"github.com/dolthub/dolt/go/store/spec"
    35  	"github.com/dolthub/dolt/go/store/types"
    36  )
    37  
    38  func nomsMap(ctx context.Context, noms *kingpin.Application) (*kingpin.CmdClause, util.KingpinHandler) {
    39  	maap := noms.Command("map", "interact with maps")
    40  
    41  	mapNew := maap.Command("new", "creates a new map")
    42  	newDb := mapNew.Arg("database", "spec to db to create map within").Required().String()
    43  	newEntries := mapNew.Arg("entries", "key/value pairs for entries").Strings()
    44  
    45  	mapSet := maap.Command("set", "sets one or more keys in a map")
    46  	setSpec := mapSet.Arg("spec", "value spec for the map to edit").Required().String()
    47  	setEntries := mapSet.Arg("entries", "key/value pairs for entries").Strings()
    48  
    49  	mapDel := maap.Command("del", "removes one or more entries from a map")
    50  	delSpec := mapDel.Arg("spec", "value spec for the map to edit").Required().String()
    51  	delKeys := mapDel.Arg("keys", "keys for the entries to be removed").Strings()
    52  
    53  	return maap, func(input string) int {
    54  		switch input {
    55  		case mapNew.FullCommand():
    56  			return nomsMapNew(ctx, *newDb, *newEntries)
    57  		case mapSet.FullCommand():
    58  			return nomsMapSet(ctx, *setSpec, *setEntries)
    59  		case mapDel.FullCommand():
    60  			return nomsMapDel(ctx, *delSpec, *delKeys)
    61  		}
    62  		d.Panic("notreached")
    63  		return 1
    64  	}
    65  }
    66  
    67  func nomsMapNew(ctx context.Context, dbStr string, args []string) int {
    68  	sp, err := spec.ForDatabase(dbStr)
    69  	d.PanicIfError(err)
    70  	db := sp.GetDatabase(ctx)
    71  	m, err := types.NewMap(ctx, db)
    72  	d.PanicIfError(err)
    73  	applyMapEdits(ctx, db, sp, m, nil, args)
    74  	return 0
    75  }
    76  
    77  func nomsMapSet(ctx context.Context, specStr string, args []string) int {
    78  	sp, err := spec.ForPath(specStr)
    79  	d.PanicIfError(err)
    80  	db := sp.GetDatabase(ctx)
    81  	rootVal, basePath := splitPath(ctx, db, sp)
    82  	applyMapEdits(ctx, db, sp, rootVal, basePath, args)
    83  	return 0
    84  }
    85  
    86  func nomsMapDel(ctx context.Context, specStr string, args []string) int {
    87  	sp, err := spec.ForPath(specStr)
    88  	d.PanicIfError(err)
    89  
    90  	db := sp.GetDatabase(ctx)
    91  	rootVal, basePath := splitPath(ctx, db, sp)
    92  	patch := diff.Patch{}
    93  	for i := 0; i < len(args); i++ {
    94  		kp := parseKeyPart(args, i)
    95  		patch = append(patch, diff.Difference{
    96  			Path:       append(basePath, kp),
    97  			ChangeType: types.DiffChangeRemoved,
    98  		})
    99  	}
   100  
   101  	appplyPatch(ctx, db, sp, rootVal, basePath, patch)
   102  	return 0
   103  }
   104  
   105  func applyMapEdits(ctx context.Context, db datas.Database, sp spec.Spec, rootVal types.Value, basePath types.Path, args []string) {
   106  	if len(args)%2 != 0 {
   107  		util.CheckError(fmt.Errorf("Must be an even number of key/value pairs"))
   108  	}
   109  	if rootVal == nil {
   110  		util.CheckErrorNoUsage(fmt.Errorf("No value at: %s", sp.String()))
   111  		return
   112  	}
   113  	patch := diff.Patch{}
   114  	for i := 0; i < len(args); i += 2 {
   115  		kp := parseKeyPart(args, i)
   116  		vv, err := argumentToValue(ctx, args[i+1], db)
   117  		if err != nil {
   118  			util.CheckError(fmt.Errorf("Invalid value: %s at position %d: %s", args[i+1], i+1, err))
   119  		}
   120  		patch = append(patch, diff.Difference{
   121  			Path:       append(basePath, kp),
   122  			ChangeType: types.DiffChangeModified,
   123  			NewValue:   vv,
   124  		})
   125  	}
   126  	appplyPatch(ctx, db, sp, rootVal, basePath, patch)
   127  }
   128  
   129  func parseKeyPart(args []string, i int) (res types.PathPart) {
   130  	idx, h, rem, err := types.ParsePathIndex(args[i])
   131  	if rem != "" {
   132  		util.CheckError(fmt.Errorf("Invalid key: %s at position %d", args[i], i))
   133  	}
   134  	if err != nil {
   135  		util.CheckError(fmt.Errorf("Invalid key: %s at position %d: %s", args[i], i, err))
   136  	}
   137  	if idx != nil {
   138  		res = types.NewIndexPath(idx)
   139  	} else {
   140  		res = types.NewHashIndexPath(h)
   141  	}
   142  	return
   143  }