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 }