github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/cmd/noms/noms_set.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  	"bytes"
    26  	"context"
    27  	"fmt"
    28  	"strconv"
    29  
    30  	"github.com/attic-labs/kingpin"
    31  
    32  	"github.com/dolthub/dolt/go/store/cmd/noms/util"
    33  	"github.com/dolthub/dolt/go/store/d"
    34  	"github.com/dolthub/dolt/go/store/datas"
    35  	"github.com/dolthub/dolt/go/store/diff"
    36  	"github.com/dolthub/dolt/go/store/spec"
    37  	"github.com/dolthub/dolt/go/store/types"
    38  )
    39  
    40  func nomsSet(ctx context.Context, noms *kingpin.Application) (*kingpin.CmdClause, util.KingpinHandler) {
    41  	set := noms.Command("set", "interact with sets")
    42  
    43  	setNew := set.Command("new", "creates a new set")
    44  	newDb := setNew.Arg("database", "spec to db to create set within").Required().String()
    45  	newEntries := setNew.Arg("items", "items to insert").Strings()
    46  
    47  	setInsert := set.Command("insert", "inserts one or more items into a set")
    48  	insertSpec := setInsert.Arg("spec", "value spec for the set to edit").Required().String()
    49  	insertEntries := setInsert.Arg("items", "items to insert").Strings()
    50  
    51  	setDel := set.Command("del", "removes one or more items from a set")
    52  	delSpec := setDel.Arg("spec", "value spec for the set to edit").Required().String()
    53  	delEntries := setDel.Arg("items", "items to delete").Strings()
    54  
    55  	return set, func(input string) int {
    56  		switch input {
    57  		case setNew.FullCommand():
    58  			return nomsSetNew(ctx, *newDb, *newEntries)
    59  		case setInsert.FullCommand():
    60  			return nomsSetInsert(ctx, *insertSpec, *insertEntries)
    61  		case setDel.FullCommand():
    62  			return nomsSetDel(ctx, *delSpec, *delEntries)
    63  		}
    64  		d.Panic("notreached")
    65  		return 1
    66  	}
    67  }
    68  
    69  func nomsSetNew(ctx context.Context, dbStr string, args []string) int {
    70  	sp, err := spec.ForDatabase(dbStr)
    71  	d.PanicIfError(err)
    72  	db := sp.GetDatabase(ctx)
    73  	s, err := types.NewSet(ctx, db)
    74  	d.PanicIfError(err)
    75  	applySetEdits(ctx, sp, s, nil, types.DiffChangeAdded, args)
    76  	return 0
    77  }
    78  
    79  func nomsSetInsert(ctx context.Context, specStr string, args []string) int {
    80  	sp, err := spec.ForPath(specStr)
    81  	d.PanicIfError(err)
    82  	db := sp.GetDatabase(ctx)
    83  	rootVal, basePath := splitPath(ctx, db, sp)
    84  	applySetEdits(ctx, sp, rootVal, basePath, types.DiffChangeAdded, args)
    85  	return 0
    86  }
    87  
    88  func nomsSetDel(ctx context.Context, specStr string, args []string) int {
    89  	sp, err := spec.ForPath(specStr)
    90  	d.PanicIfError(err)
    91  	db := sp.GetDatabase(ctx)
    92  	rootVal, basePath := splitPath(ctx, db, sp)
    93  	applySetEdits(ctx, sp, rootVal, basePath, types.DiffChangeRemoved, args)
    94  	return 0
    95  }
    96  
    97  func applySetEdits(ctx context.Context, sp spec.Spec, rootVal types.Value, basePath types.Path, ct types.DiffChangeType, args []string) {
    98  	if rootVal == nil {
    99  		util.CheckErrorNoUsage(fmt.Errorf("No value at: %s", sp.String()))
   100  		return
   101  	}
   102  	db := sp.GetDatabase(ctx)
   103  	patch := diff.Patch{}
   104  	for i := 0; i < len(args); i++ {
   105  		vv, err := argumentToValue(ctx, args[i], db)
   106  		if err != nil {
   107  			util.CheckErrorNoUsage(err)
   108  		}
   109  		var pp types.PathPart
   110  		if types.ValueCanBePathIndex(vv) {
   111  			pp = types.NewIndexPath(vv)
   112  		} else {
   113  			h, err := vv.Hash(db.Format())
   114  			d.PanicIfError(err)
   115  			pp = types.NewHashIndexPath(h)
   116  		}
   117  		d := diff.Difference{
   118  			Path: append(basePath, pp),
   119  		}
   120  		if ct == types.DiffChangeAdded {
   121  			d.NewValue = vv
   122  		} else {
   123  			d.OldValue = vv
   124  		}
   125  		patch = append(patch, d)
   126  	}
   127  	appplyPatch(ctx, db, sp, rootVal, basePath, patch)
   128  }
   129  
   130  func argumentToValue(ctx context.Context, arg string, db datas.Database) (types.Value, error) {
   131  	d.PanicIfTrue(arg == "")
   132  
   133  	if arg == "true" {
   134  		return types.Bool(true), nil
   135  	}
   136  	if arg == "false" {
   137  		return types.Bool(false), nil
   138  	}
   139  	if arg[0] == '"' {
   140  		buf := bytes.Buffer{}
   141  		for i := 1; i < len(arg); i++ {
   142  			c := arg[i]
   143  			if c == '"' {
   144  				if i != len(arg)-1 {
   145  					break
   146  				}
   147  				return types.String(buf.String()), nil
   148  			}
   149  			if c == '\\' {
   150  				i++
   151  				c = arg[i]
   152  				if c != '\\' && c != '"' {
   153  					return nil, fmt.Errorf("Invalid string argument: %s: Only '\\' and '\"' can be escaped", arg)
   154  				}
   155  			}
   156  			buf.WriteByte(c)
   157  		}
   158  		return nil, fmt.Errorf("Invalid string argument: %s", arg)
   159  	}
   160  	if arg[0] == '@' {
   161  		p, err := spec.NewAbsolutePath(arg[1:])
   162  		d.PanicIfError(err)
   163  		return p.Resolve(ctx, db), nil
   164  	}
   165  	if n, err := strconv.ParseFloat(arg, 64); err == nil {
   166  		return types.Float(n), nil
   167  	}
   168  
   169  	return types.String(arg), nil
   170  }