github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/cmd/noms/noms_list.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/attic-labs/noms/cmd/util"
    13  	"github.com/attic-labs/noms/go/d"
    14  	"github.com/attic-labs/noms/go/diff"
    15  	"github.com/attic-labs/noms/go/spec"
    16  	"github.com/attic-labs/noms/go/types"
    17  )
    18  
    19  func nomsList(noms *kingpin.Application) (*kingpin.CmdClause, util.KingpinHandler) {
    20  	list := noms.Command("list", "Interact with lists.")
    21  
    22  	listNew := list.Command("new", "creates a new list")
    23  	newDb := listNew.Arg("database", "spec to db to create list within").Required().String()
    24  	newEntries := listNew.Arg("items", "items to insert").Strings()
    25  
    26  	listAppend := list.Command("append", "appends one or more items to a list")
    27  	appendSpec := listAppend.Arg("spec", "value spec for the list to edit").Required().String()
    28  	appendEntries := listAppend.Arg("items", "items to insert").Strings()
    29  
    30  	listInsert := list.Command("insert", "inserts one or more items into a list")
    31  	insertAt := listInsert.Arg("pos", "position to insert new items at").Required().Uint64()
    32  	insertSpec := listInsert.Arg("spec", "value spec for the list to edit").Required().String()
    33  	insertEntries := listInsert.Arg("items", "items to insert").Strings()
    34  
    35  	listDel := list.Command("del", "removes one or more items from the list")
    36  	delSpec := listDel.Arg("spec", "value spec for the list to edit").Required().String()
    37  	delPos := listDel.Arg("pos", "index to remove items at").Required().Uint64()
    38  	delLen := listDel.Arg("len", "number of items to remove").Required().Uint64()
    39  
    40  	return list, func(input string) int {
    41  		switch input {
    42  		case listNew.FullCommand():
    43  			return nomsListNew(*newDb, *newEntries)
    44  		case listAppend.FullCommand():
    45  			return nomsListAppend(*appendSpec, *appendEntries)
    46  		case listInsert.FullCommand():
    47  			return nomsListInsert(*insertSpec, *insertAt, *insertEntries)
    48  		case listDel.FullCommand():
    49  			return nomsListDel(*delSpec, *delPos, *delLen)
    50  		}
    51  		d.Panic("notreached")
    52  		return 1
    53  	}
    54  }
    55  
    56  func nomsListNew(dbStr string, args []string) int {
    57  	sp, err := spec.ForDatabase(dbStr)
    58  	d.PanicIfError(err)
    59  	applyListInserts(sp, types.NewList(sp.GetDatabase()), nil, 0, args)
    60  	return 0
    61  }
    62  
    63  func nomsListAppend(specStr string, args []string) int {
    64  	sp, err := spec.ForPath(specStr)
    65  	d.PanicIfError(err)
    66  	rootVal, basePath := splitPath(sp)
    67  	listVal := basePath.Resolve(rootVal, sp.GetDatabase())
    68  	if listVal == nil {
    69  		d.CheckErrorNoUsage(fmt.Errorf("no value at path: %s", specStr))
    70  	}
    71  	if list, ok := listVal.(types.List); ok {
    72  		applyListInserts(sp, rootVal, basePath, list.Len(), args)
    73  	} else {
    74  		d.CheckErrorNoUsage(fmt.Errorf("value at %s is not list", specStr))
    75  	}
    76  	return 0
    77  }
    78  
    79  func nomsListInsert(specStr string, pos uint64, args []string) int {
    80  	sp, err := spec.ForPath(specStr)
    81  	d.PanicIfError(err)
    82  	rootVal, basePath := splitPath(sp)
    83  	applyListInserts(sp, rootVal, basePath, pos, args)
    84  	return 0
    85  }
    86  
    87  func nomsListDel(specStr string, pos uint64, len uint64) int {
    88  	sp, err := spec.ForPath(specStr)
    89  	d.PanicIfError(err)
    90  
    91  	rootVal, basePath := splitPath(sp)
    92  	patch := diff.Patch{}
    93  	// TODO: if len-pos is large this will start to become problematic
    94  	for i := pos; i < pos+len; i++ {
    95  		patch = append(patch, diff.Difference{
    96  			Path:       append(basePath, types.NewIndexPath(types.Number(i))),
    97  			ChangeType: types.DiffChangeRemoved,
    98  		})
    99  	}
   100  
   101  	appplyPatch(sp, rootVal, basePath, patch)
   102  	return 0
   103  }
   104  
   105  func applyListInserts(sp spec.Spec, rootVal types.Value, basePath types.Path, pos uint64, args []string) {
   106  	if rootVal == nil {
   107  		d.CheckErrorNoUsage(fmt.Errorf("No value at: %s", sp.String()))
   108  		return
   109  	}
   110  	db := sp.GetDatabase()
   111  	patch := diff.Patch{}
   112  	for i := 0; i < len(args); i++ {
   113  		vv, err := argumentToValue(args[i], db)
   114  		if err != nil {
   115  			d.CheckError(fmt.Errorf("Invalid value: %s at position %d: %s", args[i], i, err))
   116  		}
   117  		patch = append(patch, diff.Difference{
   118  			Path:       append(basePath, types.NewIndexPath(types.Number(pos+uint64(i)))),
   119  			ChangeType: types.DiffChangeAdded,
   120  			NewValue:   vv,
   121  		})
   122  	}
   123  	appplyPatch(sp, rootVal, basePath, patch)
   124  }