github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/cmd/noms/noms_struct.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 nomsStruct(ctx context.Context, noms *kingpin.Application) (*kingpin.CmdClause, util.KingpinHandler) { 39 strukt := noms.Command("struct", "interact with structs") 40 41 struktNew := strukt.Command("new", "creates a new struct") 42 newDb := struktNew.Arg("database", "spec to db to create struct within").Required().String() 43 newName := struktNew.Flag("name", "name for new struct").String() 44 newFields := struktNew.Arg("fields", "key/value pairs for field names and values").Strings() 45 46 struktSet := strukt.Command("set", "sets one or more fields of a struct") 47 setSpec := struktSet.Arg("spec", "value spec for the struct to edit").Required().String() 48 setFields := struktSet.Arg("fields", "key/value pairs for field names and values").Strings() 49 50 struktDel := strukt.Command("del", "removes one or more fields from a struct") 51 delSpec := struktDel.Arg("spec", "value spec for the struct to edit").Required().String() 52 delFields := struktDel.Arg("fields", "fields to be removed").Strings() 53 54 return strukt, func(input string) int { 55 switch input { 56 case struktNew.FullCommand(): 57 return nomsStructNew(ctx, *newDb, *newName, *newFields) 58 case struktSet.FullCommand(): 59 return nomsStructSet(ctx, *setSpec, *setFields) 60 case struktDel.FullCommand(): 61 return nomsStructDel(ctx, *delSpec, *delFields) 62 } 63 d.Panic("notreached") 64 return 1 65 } 66 } 67 68 func nomsStructNew(ctx context.Context, dbStr string, name string, args []string) int { 69 sp, err := spec.ForDatabase(dbStr) 70 d.PanicIfError(err) 71 db := sp.GetDatabase(ctx) 72 st, err := types.NewStruct(db.Format(), name, nil) 73 d.PanicIfError(err) 74 applyStructEdits(ctx, db, sp, st, nil, args) 75 return 0 76 } 77 78 func nomsStructSet(ctx context.Context, specStr string, args []string) int { 79 sp, err := spec.ForPath(specStr) 80 d.PanicIfError(err) 81 db := sp.GetDatabase(ctx) 82 rootVal, basePath := splitPath(ctx, db, sp) 83 applyStructEdits(ctx, db, sp, rootVal, basePath, args) 84 return 0 85 } 86 87 func nomsStructDel(ctx context.Context, specStr string, args []string) int { 88 sp, err := spec.ForPath(specStr) 89 d.PanicIfError(err) 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 if !types.IsValidStructFieldName(args[i]) { 95 util.CheckError(fmt.Errorf("Invalid field name: %s at position: %d", args[i], i)) 96 } 97 patch = append(patch, diff.Difference{ 98 Path: append(basePath, types.FieldPath{Name: args[i]}), 99 ChangeType: types.DiffChangeRemoved, 100 }) 101 } 102 103 appplyPatch(ctx, db, sp, rootVal, basePath, patch) 104 return 0 105 } 106 107 func splitPath(ctx context.Context, db datas.Database, sp spec.Spec) (rootVal types.Value, basePath types.Path) { 108 rootPath := sp.Path 109 rootPath.Path = types.Path{} 110 rootVal = rootPath.Resolve(ctx, db) 111 if rootVal == nil { 112 util.CheckError(fmt.Errorf("Invalid path: %s", sp.String())) 113 return 114 } 115 basePath = sp.Path.Path 116 return 117 } 118 119 func applyStructEdits(ctx context.Context, db datas.Database, sp spec.Spec, rootVal types.Value, basePath types.Path, args []string) { 120 if len(args)%2 != 0 { 121 util.CheckError(fmt.Errorf("Must be an even number of key/value pairs")) 122 } 123 if rootVal == nil { 124 util.CheckErrorNoUsage(fmt.Errorf("No value at: %s", sp.String())) 125 return 126 } 127 patch := diff.Patch{} 128 for i := 0; i < len(args); i += 2 { 129 if !types.IsValidStructFieldName(args[i]) { 130 util.CheckError(fmt.Errorf("Invalid field name: %s at position: %d", args[i], i)) 131 } 132 nv, err := argumentToValue(ctx, args[i+1], db) 133 if err != nil { 134 util.CheckError(fmt.Errorf("Invalid field value: %s at position %d: %s", args[i+1], i+1, err)) 135 } 136 patch = append(patch, diff.Difference{ 137 Path: append(basePath, types.FieldPath{Name: args[i]}), 138 ChangeType: types.DiffChangeModified, 139 NewValue: nv, 140 }) 141 } 142 appplyPatch(ctx, db, sp, rootVal, basePath, patch) 143 } 144 145 func appplyPatch(ctx context.Context, db datas.Database, sp spec.Spec, rootVal types.Value, basePath types.Path, patch diff.Patch) { 146 baseVal, err := basePath.Resolve(ctx, rootVal, db) 147 util.CheckError(err) 148 if baseVal == nil { 149 util.CheckErrorNoUsage(fmt.Errorf("No value at: %s", sp.String())) 150 } 151 152 newRootVal, err := diff.Apply(ctx, db.Format(), rootVal, patch) 153 util.CheckError(err) 154 d.Chk.NotNil(newRootVal) 155 r, err := db.WriteValue(ctx, newRootVal) 156 util.CheckError(err) 157 db.Flush(ctx) 158 newAbsPath := spec.AbsolutePath{ 159 Hash: r.TargetHash(), 160 Path: basePath, 161 } 162 newSpec := sp 163 newSpec.Path = newAbsPath 164 fmt.Println(newSpec.String()) 165 }