github.com/ndau/noms@v1.0.5/cmd/noms/noms_struct.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  	"fmt"
     9  
    10  	"github.com/attic-labs/kingpin"
    11  
    12  	"github.com/ndau/noms/cmd/util"
    13  	"github.com/ndau/noms/go/d"
    14  	"github.com/ndau/noms/go/diff"
    15  	"github.com/ndau/noms/go/spec"
    16  	"github.com/ndau/noms/go/types"
    17  )
    18  
    19  func nomsStruct(noms *kingpin.Application) (*kingpin.CmdClause, util.KingpinHandler) {
    20  	strukt := noms.Command("struct", "Interact with structs.")
    21  
    22  	struktNew := strukt.Command("new", "creates a new struct")
    23  	newDb := struktNew.Arg("database", "spec to db to create struct within").Required().String()
    24  	newName := struktNew.Flag("name", "name for new struct").String()
    25  	newFields := struktNew.Arg("fields", "key/value pairs for field names and values").Strings()
    26  
    27  	struktSet := strukt.Command("set", "sets one or more fields of a struct")
    28  	setSpec := struktSet.Arg("spec", "value spec for the struct to edit").Required().String()
    29  	setFields := struktSet.Arg("fields", "key/value pairs for field names and values").Strings()
    30  
    31  	struktDel := strukt.Command("del", "removes one or more fields from a struct")
    32  	delSpec := struktDel.Arg("spec", "value spec for the struct to edit").Required().String()
    33  	delFields := struktDel.Arg("fields", "fields to be removed").Strings()
    34  
    35  	return strukt, func(input string) int {
    36  		switch input {
    37  		case struktNew.FullCommand():
    38  			return nomsStructNew(*newDb, *newName, *newFields)
    39  		case struktSet.FullCommand():
    40  			return nomsStructSet(*setSpec, *setFields)
    41  		case struktDel.FullCommand():
    42  			return nomsStructDel(*delSpec, *delFields)
    43  		}
    44  		d.Panic("notreached")
    45  		return 1
    46  	}
    47  }
    48  
    49  func nomsStructNew(dbStr string, name string, args []string) int {
    50  	sp, err := spec.ForDatabase(dbStr)
    51  	d.PanicIfError(err)
    52  	applyStructEdits(sp, types.NewStruct(name, nil), nil, args)
    53  	return 0
    54  }
    55  
    56  func nomsStructSet(specStr string, args []string) int {
    57  	sp, err := spec.ForPath(specStr)
    58  	d.PanicIfError(err)
    59  
    60  	rootVal, basePath := splitPath(sp)
    61  	applyStructEdits(sp, rootVal, basePath, args)
    62  	return 0
    63  }
    64  
    65  func nomsStructDel(specStr string, args []string) int {
    66  	sp, err := spec.ForPath(specStr)
    67  	d.PanicIfError(err)
    68  
    69  	rootVal, basePath := splitPath(sp)
    70  	patch := diff.Patch{}
    71  	for i := 0; i < len(args); i++ {
    72  		if !types.IsValidStructFieldName(args[i]) {
    73  			d.CheckError(fmt.Errorf("Invalid field name: %s at position: %d", args[i], i))
    74  		}
    75  		patch = append(patch, diff.Difference{
    76  			Path:       append(basePath, types.FieldPath{Name: args[i]}),
    77  			ChangeType: types.DiffChangeRemoved,
    78  		})
    79  	}
    80  
    81  	appplyPatch(sp, rootVal, basePath, patch)
    82  	return 0
    83  }
    84  
    85  func splitPath(sp spec.Spec) (rootVal types.Value, basePath types.Path) {
    86  	db := sp.GetDatabase()
    87  	rootPath := sp.Path
    88  	rootPath.Path = types.Path{}
    89  	rootVal = rootPath.Resolve(db)
    90  	if rootVal == nil {
    91  		d.CheckError(fmt.Errorf("Invalid path: %s", sp.String()))
    92  		return
    93  	}
    94  	basePath = sp.Path.Path
    95  	return
    96  }
    97  
    98  func applyStructEdits(sp spec.Spec, rootVal types.Value, basePath types.Path, args []string) {
    99  	if len(args)%2 != 0 {
   100  		d.CheckError(fmt.Errorf("Must be an even number of key/value pairs"))
   101  	}
   102  	if rootVal == nil {
   103  		d.CheckErrorNoUsage(fmt.Errorf("No value at: %s", sp.String()))
   104  		return
   105  	}
   106  	db := sp.GetDatabase()
   107  	patch := diff.Patch{}
   108  	for i := 0; i < len(args); i += 2 {
   109  		if !types.IsValidStructFieldName(args[i]) {
   110  			d.CheckError(fmt.Errorf("Invalid field name: %s at position: %d", args[i], i))
   111  		}
   112  		nv, err := argumentToValue(args[i+1], db)
   113  		if err != nil {
   114  			d.CheckError(fmt.Errorf("Invalid field value: %s at position %d: %s", args[i+1], i+1, err))
   115  		}
   116  		patch = append(patch, diff.Difference{
   117  			Path:       append(basePath, types.FieldPath{Name: args[i]}),
   118  			ChangeType: types.DiffChangeModified,
   119  			NewValue:   nv,
   120  		})
   121  	}
   122  	appplyPatch(sp, rootVal, basePath, patch)
   123  }
   124  
   125  func appplyPatch(sp spec.Spec, rootVal types.Value, basePath types.Path, patch diff.Patch) {
   126  	db := sp.GetDatabase()
   127  	baseVal := basePath.Resolve(rootVal, db)
   128  	if baseVal == nil {
   129  		d.CheckErrorNoUsage(fmt.Errorf("No value at: %s", sp.String()))
   130  	}
   131  
   132  	newRootVal := diff.Apply(rootVal, patch)
   133  	d.Chk.NotNil(newRootVal)
   134  	r := db.WriteValue(newRootVal)
   135  	db.Flush()
   136  	newAbsPath := spec.AbsolutePath{
   137  		Hash: r.TargetHash(),
   138  		Path: basePath,
   139  	}
   140  	fmt.Println(newAbsPath.String())
   141  }