github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/cmd/noms/noms_set.go (about)

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