diff --git a/bolt_model.go b/bolt_model.go index d619b2c..52c53ef 100644 --- a/bolt_model.go +++ b/bolt_model.go @@ -1,11 +1,11 @@ package main import ( + "bytes" "errors" "fmt" "os" "strings" - "bytes" "github.com/boltdb/bolt" ) @@ -18,8 +18,8 @@ type BoltDB struct { } type PathNode struct { - name []byte - dataType Datatype + name []byte + dataType Datatype } /* @@ -36,13 +36,13 @@ type BoltBucket struct { } func (b *BoltBucket) SetName(nm []byte) { - b.name = nm - for _, dtk := range dataTypeNameSlice { - if _, err := datatypes[dtk].ToString(nm); err == nil { - b.nameDatatype = datatypes[dtk] - break - } - } + b.name = nm + for _, dtk := range dataTypeNameSlice { + if _, err := datatypes[dtk].ToString(nm); err == nil { + b.nameDatatype = datatypes[dtk] + break + } + } } /* @@ -57,27 +57,27 @@ type BoltPair struct { } func NewBoltPair(parent *BoltBucket, key, val []byte) *BoltPair { - tp := BoltPair{ - key: key, - val: val, - } - for _, dtk := range dataTypeNameSlice { - if _, err := datatypes[dtk].ToString(key); err == nil { - tp.keyDatatype = datatypes[dtk] - break - } - } - for _, dtk := range dataTypeNameSlice { - if _, err := datatypes[dtk].ToString(val); err == nil { - tp.valDatatype = datatypes[dtk] - break - } - } - return &tp + tp := BoltPair{ + key: key, + val: val, + } + for _, dtk := range dataTypeNameSlice { + if _, err := datatypes[dtk].ToString(key); err == nil { + tp.keyDatatype = datatypes[dtk] + break + } + } + for _, dtk := range dataTypeNameSlice { + if _, err := datatypes[dtk].ToString(val); err == nil { + tp.valDatatype = datatypes[dtk] + break + } + } + return &tp } -func (bd *BoltDB) getGenericFromStringPath(path []string) (*BoltBucket, *BoltPair, error) { - return bd.getGenericFromPath(stringPathToBytePath(path)) +func (bd *BoltDB) getGenericFromStringPath(path []PathNode) (*BoltBucket, *BoltPair, error) { + return bd.getGenericFromPath(path) } func (bd *BoltDB) getGenericFromPath(path []PathNode) (*BoltBucket, *BoltPair, error) { @@ -167,7 +167,7 @@ func (bd *BoltDB) getVisibleItemCount(path []PathNode) (int, error) { } func (bd *BoltDB) buildVisiblePathSlice() ([][]PathNode, error) { - var retSlice [][]string + var retSlice [][]PathNode var retErr error // The root path, recurse for root buckets for i := range bd.buckets { @@ -260,7 +260,7 @@ func (bd *BoltDB) openBucket(path []PathNode) error { func (bd *BoltDB) getBucket(k []byte) (*BoltBucket, error) { for i := range bd.buckets { - if bytes.Equal(bd.buckets[i].name, k) { + if bytes.Equal(bd.buckets[i].name, k) { return &bd.buckets[i], nil } } @@ -278,7 +278,7 @@ func (bd *BoltDB) syncOpenBuckets(shadow *BoltDB) { // First test this bucket for i := range bd.buckets { for j := range shadow.buckets { - if bytes.Equal(bd.buckets[i].name, shadow.buckets[j].name) { + if bytes.Equal(bd.buckets[i].name, shadow.buckets[j].name) { bd.buckets[i].syncOpenBuckets(&shadow.buckets[j]) } } @@ -292,7 +292,7 @@ func (bd *BoltDB) refreshDatabase() *BoltDB { return tx.ForEach(func(nm []byte, b *bolt.Bucket) error { bb, err := readBucket(b) if err == nil { - bb.SetName(nm) + bb.SetName(nm) bb.expanded = false memBolt.buckets = append(memBolt.buckets, *bb) return nil @@ -308,21 +308,22 @@ GetStringName returns the name of the bucket after passing it through it's Datatype ToString function */ func (b *BoltBucket) GetStringName() string { - ret, err := b.nameDatatype.ToString(b.name) - if err != nil { - return stringify(b.name) - } - return ret + ret, err := b.nameDatatype.ToString(b.name) + if err != nil { + return stringify(b.name) + } + return ret } /* GetPath returns the database path leading to this BoltBucket */ -func (b *BoltBucket) GetPath() [][]byte { +func (b *BoltBucket) GetPath() []PathNode { + bktPath := PathNode{name: b.name, dataType: b.nameDatatype} if b.parent != nil { - return append(b.parent.GetPath(), b.name) + return append(b.parent.GetPath(), bktPath) } - return [][]byte{b.name} + return []PathNode{bktPath} } /* @@ -332,7 +333,7 @@ The passed prefix is the path leading to the current bucket func (b *BoltBucket) buildVisiblePathSlice(prefix []PathNode) ([][]PathNode, error) { var retSlice [][]PathNode var retErr error - bucketNode := PathNode{b.name, b.nameDatatype} + bucketNode := PathNode{b.name, b.nameDatatype} retSlice = append(retSlice, append(prefix, bucketNode)) if b.expanded { // Add subbuckets @@ -356,7 +357,7 @@ func (b *BoltBucket) syncOpenBuckets(shadow *BoltBucket) { b.expanded = shadow.expanded for i := range b.buckets { for j := range shadow.buckets { - if bytes.Equal(b.buckets[i].name, shadow.buckets[j].name) { + if bytes.Equal(b.buckets[i].name, shadow.buckets[j].name) { b.buckets[i].syncOpenBuckets(&shadow.buckets[j]) } } @@ -393,28 +394,30 @@ GetStringKey returns the key of the pair after passing it through it's Datatype ToString function */ func (p *BoltPair) GetStringKey() string { - ret, err := p.keyDatatype.ToString(p.key) - if err != nil { - return stringify(p.key) - } - return ret + ret, err := p.keyDatatype.ToString(p.key) + if err != nil { + return stringify(p.key) + } + return ret } + /* GetStringVal returns the val of the pair after passing it through it's Datatype ToString function */ func (p *BoltPair) GetStringVal() string { - ret, err := p.valDatatype.ToString(p.val) - if err != nil { - return stringify(p.val) - } - return ret + ret, err := p.valDatatype.ToString(p.val) + if err != nil { + return stringify(p.val) + } + return ret } + /* GetPath Returns the path of the BoltPair */ -func (p *BoltPair) GetPath() [][]byte { - return append(p.parent.GetPath(), p.key) +func (p *BoltPair) GetPath() []PathNode { + return append(p.parent.GetPath(), PathNode{name: p.key, dataType: p.keyDatatype}) } /* This is a go-between function (between the boltbrowser structs @@ -424,9 +427,9 @@ func (p *BoltPair) GetPath() [][]byte { * Mainly used for moving a bucket from one path to another * as in the 'renameBucket' function below. */ -func addBucketFromBoltBucket(path [][]byte, bb *BoltBucket) error { +func addBucketFromBoltBucket(path []PathNode, bb *BoltBucket) error { if err := insertBucket(path, bb.name); err == nil { - bucketPath := append(path, bb.name) + bucketPath := append(path, PathNode{name: bb.name, dataType: bb.nameDatatype}) for i := range bb.pairs { if err = insertPair(bucketPath, bb.pairs[i].key, bb.pairs[i].val); err != nil { return err @@ -441,7 +444,7 @@ func addBucketFromBoltBucket(path [][]byte, bb *BoltBucket) error { return nil } -func deleteKey(path [][]byte) error { +func deleteKey(path []PathNode) error { if AppArgs.ReadOnly { return errors.New("DB is in Read-Only Mode") } @@ -450,13 +453,13 @@ func deleteKey(path [][]byte) error { // the rest are buckets leading to that key if len(path) == 1 { // Deleting a root bucket - return tx.DeleteBucket(path[0]) + return tx.DeleteBucket(path[0].name) } - b := tx.Bucket(path[0]) + b := tx.Bucket(path[0].name) if b != nil { if len(path) > 1 { for i := range path[1 : len(path)-1] { - b = b.Bucket(path[i+1]) + b = b.Bucket(path[i+1].name) if b == nil { return errors.New("deleteKey: Invalid Path") } @@ -464,11 +467,11 @@ func deleteKey(path [][]byte) error { } // Now delete the last key in the path var err error - if deleteBkt := b.Bucket(path[len(path)-1]); deleteBkt == nil { + if deleteBkt := b.Bucket(path[len(path)-1].name); deleteBkt == nil { // Must be a pair - err = b.Delete(path[len(path)-1]) + err = b.Delete(path[len(path)-1].name) } else { - err = b.DeleteBucket(path[len(path)-1]) + err = b.DeleteBucket(path[len(path)-1].name) } return err } @@ -484,9 +487,9 @@ func readBucket(b *bolt.Bucket) (*BoltBucket, error) { b.ForEach(func(k, v []byte) error { if v == nil { tb, err := readBucket(b.Bucket(k)) - tb.parent = bb if err == nil { - tb.SetName(k) + tb.parent = bb + tb.SetName(k) bb.buckets = append(bb.buckets, *tb) } } else { @@ -497,8 +500,8 @@ func readBucket(b *bolt.Bucket) (*BoltBucket, error) { return bb, nil } -func renameBucket(path [][]byte, name []byte) error { - if bytes.Equal(name, path[len(path)-1]) { +func renameBucket(path []PathNode, name []byte) error { + if bytes.Equal(name, path[len(path)-1].name) { // No change requested return nil } @@ -506,11 +509,11 @@ func renameBucket(path [][]byte, name []byte) error { err := db.View(func(tx *bolt.Tx) error { // len(b.path)-1 is the key we need to delete, // the rest are buckets leading to that key - b := tx.Bucket(path[0]) + b := tx.Bucket(path[0].name) if b != nil { if len(path) > 1 { for i := range path[1:len(path)] { - b = b.Bucket(path[i+1]) + b = b.Bucket(path[i+1].name) if b == nil { return errors.New("renameBucket: Invalid Path") } @@ -539,7 +542,7 @@ func renameBucket(path [][]byte, name []byte) error { return err } // Rechristen our cached bucket - bb.SetName(name) + bb.SetName(name) // And re-add it parentPath := path[:len(path)-1] @@ -549,26 +552,26 @@ func renameBucket(path [][]byte, name []byte) error { return nil } -func updatePairKey(path [][]byte, k []byte) error { +func updatePairKey(path []PathNode, k []byte) error { if AppArgs.ReadOnly { return errors.New("DB is in Read-Only Mode") } err := db.Update(func(tx *bolt.Tx) error { // len(b.path)-1 is the key for the pair we're updating, // the rest are buckets leading to that key - b := tx.Bucket(path[0]) + b := tx.Bucket(path[0].name) if b != nil { if len(path) > 0 { for i := range path[1 : len(path)-1] { - b = b.Bucket(path[i+1]) + b = b.Bucket(path[i+1].name) if b == nil { return errors.New("updatePairValue: Invalid Path") } } } bk := path[len(path)-1] - v := b.Get(bk) - err := b.Delete(bk) + v := b.Get(bk.name) + err := b.Delete(bk.name) if err == nil { // Old pair has been deleted, now add the new one err = b.Put(k, v) @@ -581,25 +584,25 @@ func updatePairKey(path [][]byte, k []byte) error { return err } -func updatePairValue(path [][]byte, v []byte) error { +func updatePairValue(path []PathNode, v []byte) error { if AppArgs.ReadOnly { return errors.New("DB is in Read-Only Mode") } err := db.Update(func(tx *bolt.Tx) error { // len(b.GetPath())-1 is the key for the pair we're updating, // the rest are buckets leading to that key - b := tx.Bucket(path[0]) + b := tx.Bucket(path[0].name) if b != nil { if len(path) > 0 { for i := range path[1 : len(path)-1] { - b = b.Bucket(path[i+1]) + b = b.Bucket(path[i+1].name) if b == nil { return errors.New("updatePairValue: Invalid Path") } } } // Now update the last key in the path - err := b.Put(path[len(path)-1], v) + err := b.Put(path[len(path)-1].name, v) return err } return errors.New("updatePairValue: Invalid Path") @@ -607,7 +610,7 @@ func updatePairValue(path [][]byte, v []byte) error { return err } -func insertBucket(path [][]byte, n []byte) error { +func insertBucket(path []PathNode, n []byte) error { if AppArgs.ReadOnly { return errors.New("DB is in Read-Only Mode") } @@ -621,12 +624,12 @@ func insertBucket(path [][]byte, n []byte) error { } } else { rootBucket, path := path[0], path[1:] - b := tx.Bucket(rootBucket) + b := tx.Bucket(rootBucket.name) if b != nil { for len(path) > 0 { - var tstBucket []byte + var tstBucket PathNode tstBucket, path = path[0], path[1:] - nB := b.Bucket(tstBucket) + nB := b.Bucket(tstBucket.name) if nB == nil { // Not a bucket, if we're out of path, just move on if len(path) != 0 { @@ -647,7 +650,7 @@ func insertBucket(path [][]byte, n []byte) error { return err } -func insertPair(path [][]byte, k []byte, v []byte) error { +func insertPair(path []PathNode, k []byte, v []byte) error { if AppArgs.ReadOnly { return errors.New("DB is in Read-Only Mode") } @@ -658,11 +661,11 @@ func insertPair(path [][]byte, k []byte, v []byte) error { return errors.New("insertPair: Cannot insert pair at root") } var err error - b := tx.Bucket(path[0]) + b := tx.Bucket(path[0].name) if b != nil { if len(path) > 0 { for i := 1; i < len(path); i++ { - b = b.Bucket(path[i]) + b = b.Bucket(path[i].name) if b == nil { return fmt.Errorf("insertPair: %s", err) } @@ -679,63 +682,79 @@ func insertPair(path [][]byte, k []byte, v []byte) error { } func stringPathToBytePath(path []string) [][]byte { - var ret [][]byte - for _, v := range path { - ret = append(ret, []byte(v)) - } - return ret + var ret [][]byte + for _, v := range path { + ret = append(ret, []byte(v)) + } + return ret } func bytePathToStringPath(path [][]byte) []string { - var ret []string - for _, v := range path { - ret = append(ret, string(v)) - } - return ret + var ret []string + for _, v := range path { + ret = append(ret, string(v)) + } + return ret } -func exportValue(path [][]byte, fName string) error { +func nodePathToStringPath(path []PathNode) []string { + var ret []string + for _, v := range path { + add, err := v.dataType.ToString(v.name) + if err != nil { + add = "ERROR" + } + ret = append(ret, add) + } + return ret +} + +func exportValue(path []PathNode, fName string) error { return db.View(func(tx *bolt.Tx) error { // len(b.path)-1 is the key whose value we want to export // the rest are buckets leading to that key - b := tx.Bucket(path[0]) + b := tx.Bucket(path[0].name) if b != nil { if len(path) > 1 { for i := range path[1 : len(path)-1] { - b = b.Bucket(path[i+1]) + b = b.Bucket(path[i+1].name) if b == nil { - return errors.New("exportValue: Invalid Path: " + strings.Join(bytePathToStringPath(path), "/")) + return errors.New("exportValue: Invalid Path: " + strings.Join(nodePathToStringPath(path), "/")) } } } bk := path[len(path)-1] - v := b.Get(bk) + v := b.Get(bk.name) return writeToFile(fName, string(v)+"\n", os.O_CREATE|os.O_WRONLY|os.O_TRUNC) } return errors.New("exportValue: Invalid Bucket") }) } -func exportJSON(path [][]byte, fName string) error { +func exportJSON(path []PathNode, fName string) error { return db.View(func(tx *bolt.Tx) error { // len(b.path)-1 is the key whose value we want to export // the rest are buckets leading to that key - b := tx.Bucket(path[0]) + b := tx.Bucket(path[0].name) if b != nil { if len(path) > 1 { for i := range path[1 : len(path)-1] { - b = b.Bucket(path[i+1]) + b = b.Bucket(path[i+1].name) if b == nil { - return errors.New("exportValue: Invalid Path: " + strings.Join(bytePathToStringPath(path), "/")) + return errors.New("exportValue: Invalid Path: " + strings.Join(nodePathToStringPath(path), "/")) } } } bk := path[len(path)-1] - if v := b.Get(bk); v != nil { - return writeToFile(fName, "{\""+string(bk)+"\":\""+string(v)+"\"}", os.O_CREATE|os.O_WRONLY|os.O_TRUNC) + if v := b.Get(bk.name); v != nil { + str, err := bk.dataType.ToString(bk.name) + if err != nil { + str = "ERROR" + } + return writeToFile(fName, "{\""+str+"\":\""+string(v)+"\"}", os.O_CREATE|os.O_WRONLY|os.O_TRUNC) } - if b.Bucket(bk) != nil { - return writeToFile(fName, genJSONString(b.Bucket(bk)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC) + if b.Bucket(bk.name) != nil { + return writeToFile(fName, genJSONString(b.Bucket(bk.name)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC) } return writeToFile(fName, genJSONString(b), os.O_CREATE|os.O_WRONLY|os.O_TRUNC) } diff --git a/screen_browser.go b/screen_browser.go index ded1c62..81981ca 100644 --- a/screen_browser.go +++ b/screen_browser.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "encoding/json" "fmt" "strings" @@ -40,7 +41,7 @@ type BrowserScreen struct { leftPaneBuffer []Line rightPaneBuffer []Line - niceLog []string + niceLog []string } /* @@ -164,7 +165,7 @@ func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int { } else if event.Key == termbox.KeyEnter { b, p, _ := screen.db.getGenericFromStringPath(screen.currentPath) if b != nil { - screen.db.toggleOpenBucket(stringPathToBytePath(screen.currentPath)) + screen.db.toggleOpenBucket(screen.currentPath) } else if p != nil { screen.startEditItem() } @@ -173,7 +174,7 @@ func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int { b, p, _ := screen.db.getGenericFromStringPath(screen.currentPath) // Select the current item if b != nil { - screen.db.toggleOpenBucket(stringPathToBytePath(screen.currentPath)) + screen.db.toggleOpenBucket(screen.currentPath) } else if p != nil { screen.startEditItem() } else { @@ -184,17 +185,17 @@ func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int { // If we are _on_ a bucket that's open, close it b, _, e := screen.db.getGenericFromStringPath(screen.currentPath) if e == nil && b != nil && b.expanded { - screen.db.closeBucket(stringPathToBytePath(screen.currentPath)) + screen.db.closeBucket(screen.currentPath) } else { if len(screen.currentPath) > 1 { - parentBucket, err := screen.db.getBucketFromPath(stringPathToBytePath(screen.currentPath[:len(screen.currentPath)-1])) + parentBucket, err := screen.db.getBucketFromPath(screen.currentPath[:len(screen.currentPath)-1]) if err == nil { screen.db.closeBucket(parentBucket.GetPath()) // Figure out how far up we need to move the cursor - screen.currentPath = bytePathToStringPath(parentBucket.GetPath()) + screen.currentPath = parentBucket.GetPath() } } else { - screen.db.closeBucket(stringPathToBytePath(screen.currentPath)) + screen.db.closeBucket(screen.currentPath) } } @@ -209,24 +210,24 @@ func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int { } else if event.Ch == 't' { // TODO: change the type of the pair value being viewed - p, err := screen.db.getPairFromPath(stringPathToBytePath(screen.currentPath)) - if err != nil { - screen.setMessage("Cannot change pair type (did you mean 'T': Change pair key/bucket name type?)") - return BrowserScreenIndex - } - p.valDatatype = findNextDatatype(p.valDatatype.Name()) + p, err := screen.db.getPairFromPath(screen.currentPath) + if err != nil { + screen.setMessage("Cannot change pair type (did you mean 'T': Change pair key/bucket name type?)") + return BrowserScreenIndex + } + p.valDatatype = findNextDatatype(p.valDatatype.Name()) - } else if event.Ch == 'T' { - b, p, err := screen.db.getGenericFromStringPath(screen.currentPath) - if err != nil { - screen.setMessage("Error reading path... Not sure what to do.") - return BrowserScreenIndex - } - if b != nil { - b.nameDatatype = findNextDatatype(b.nameDatatype.Name()) - } else if p != nil { - p.keyDatatype = findNextDatatype(p.keyDatatype.Name()) - } + } else if event.Ch == 'T' { + b, p, err := screen.db.getGenericFromStringPath(screen.currentPath) + if err != nil { + screen.setMessage("Error reading path... Not sure what to do.") + return BrowserScreenIndex + } + if b != nil { + b.nameDatatype = findNextDatatype(b.nameDatatype.Name()) + } else if p != nil { + p.keyDatatype = findNextDatatype(p.keyDatatype.Name()) + } } return BrowserScreenIndex } @@ -242,11 +243,11 @@ func (screen *BrowserScreen) handleInputKeyEvent(event termbox.Event) int { if b != nil { if screen.mode == modeChangeKey { newName := screen.inputModal.GetValue() - if renameBucket(stringPathToBytePath(screen.currentPath), []byte(newName)) != nil { + if renameBucket(screen.currentPath, []byte(newName)) != nil { screen.setMessage("Error renaming bucket.") } else { b.name = []byte(newName) - screen.currentPath[len(screen.currentPath)-1] = newName + screen.currentPath[len(screen.currentPath)-1].name = b.name screen.setMessage("Bucket Renamed!") screen.refreshDatabase() } @@ -254,17 +255,17 @@ func (screen *BrowserScreen) handleInputKeyEvent(event termbox.Event) int { } else if p != nil { if screen.mode == modeChangeKey { newKey := screen.inputModal.GetValue() - if updatePairKey(stringPathToBytePath(screen.currentPath), []byte(newKey)) != nil { + if updatePairKey(screen.currentPath, []byte(newKey)) != nil { screen.setMessage("Error occurred updating Pair.") } else { p.key = []byte(newKey) - screen.currentPath[len(screen.currentPath)-1] = newKey + screen.currentPath[len(screen.currentPath)-1].name = p.key screen.setMessage("Pair updated!") screen.refreshDatabase() } } else if screen.mode == modeChangeVal { newVal := screen.inputModal.GetValue() - if updatePairValue(stringPathToBytePath(screen.currentPath), []byte(newVal)) != nil { + if updatePairValue(screen.currentPath, []byte(newVal)) != nil { screen.setMessage("Error occurred updating Pair.") } else { p.val = []byte(newVal) @@ -286,13 +287,13 @@ func (screen *BrowserScreen) handleDeleteKeyEvent(event termbox.Event) int { if screen.confirmModal.IsAccepted() { holdNextPath := screen.db.getNextVisiblePath(screen.currentPath) holdPrevPath := screen.db.getPrevVisiblePath(screen.currentPath) - if deleteKey(stringPathToBytePath(screen.currentPath)) == nil { + if deleteKey(screen.currentPath) == nil { screen.refreshDatabase() // Move the current path endpoint appropriately //found_new_path := false if holdNextPath != nil { if len(holdNextPath) > 2 { - if holdNextPath[len(holdNextPath)-2] == screen.currentPath[len(screen.currentPath)-2] { + if bytes.Equal(holdNextPath[len(holdNextPath)-2].name, screen.currentPath[len(screen.currentPath)-2].name) { screen.currentPath = holdNextPath } else if holdPrevPath != nil { screen.currentPath = holdPrevPath @@ -328,12 +329,21 @@ func (screen *BrowserScreen) handleInsertKeyEvent(event termbox.Event) int { screen.inputModal.HandleEvent(event) if screen.inputModal.IsDone() { newVal := screen.inputModal.GetValue() + // TODO: Just default datatype to string? Or should we infer it somehow? + useType := datatypes["string"] + nodeName, err := useType.FromString(newVal) + if err != nil { + screen.setMessage("Error Inserting new item. Data did not match datatype.") + return BrowserScreenIndex + } + newNode := PathNode{name: nodeName, dataType: useType} screen.inputModal.Clear() - var insertPath []string + var insertPath []PathNode if len(screen.currentPath) > 0 { _, p, e := screen.db.getGenericFromStringPath(screen.currentPath) if e != nil { screen.setMessage("Error Inserting new item. Invalid Path.") + return BrowserScreenIndex } insertPath = screen.currentPath // where are we inserting? @@ -345,14 +355,14 @@ func (screen *BrowserScreen) handleInsertKeyEvent(event termbox.Event) int { if len(screen.currentPath) > 1 { insertPath = screen.currentPath[:len(screen.currentPath)-1] } else { - insertPath = make([]string, 0) + insertPath = make([]PathNode, 0) } } } parentB, _, _ := screen.db.getGenericFromStringPath(insertPath) if screen.mode&modeInsertBucket == modeInsertBucket { - err := insertBucket(stringPathToBytePath(insertPath), []byte(newVal)) + err := insertBucket(insertPath, newNode.name) if err != nil { screen.setMessage(fmt.Sprintf("%s => %s", err, insertPath)) } else { @@ -360,13 +370,13 @@ func (screen *BrowserScreen) handleInsertKeyEvent(event termbox.Event) int { parentB.expanded = true } } - screen.currentPath = append(insertPath, newVal) + screen.currentPath = append(insertPath, newNode) screen.refreshDatabase() screen.mode = modeBrowse screen.inputModal.Clear() } else if screen.mode&modeInsertPair == modeInsertPair { - err := insertPair(stringPathToBytePath(insertPath), []byte(newVal), []byte{}) + err := insertPair(insertPath, []byte(newVal), []byte{}) if err != nil { screen.setMessage(fmt.Sprintf("%s => %s", err, insertPath)) screen.refreshDatabase() @@ -376,7 +386,7 @@ func (screen *BrowserScreen) handleInsertKeyEvent(event termbox.Event) int { if parentB != nil { parentB.expanded = true } - screen.currentPath = append(insertPath, newVal) + screen.currentPath = append(insertPath, newNode) screen.refreshDatabase() screen.startEditItem() } @@ -398,7 +408,7 @@ func (screen *BrowserScreen) handleExportKeyEvent(event termbox.Event) int { if screen.mode&modeExportValue == modeExportValue { // Exporting the value if p != nil { - if err := exportValue(stringPathToBytePath(screen.currentPath), fileName); err != nil { + if err := exportValue(screen.currentPath, fileName); err != nil { //screen.setMessage("Error Exporting to file " + fileName + ".") screen.setMessage(err.Error()) } else { @@ -407,7 +417,7 @@ func (screen *BrowserScreen) handleExportKeyEvent(event termbox.Event) int { } } else if screen.mode&modeExportJSON == modeExportJSON { if b != nil || p != nil { - if exportJSON(stringPathToBytePath(screen.currentPath), fileName) != nil { + if exportJSON(screen.currentPath, fileName) != nil { screen.setMessage("Error Exporting to file " + fileName + ".") } else { screen.setMessage("Value exported to file: " + fileName) @@ -429,7 +439,7 @@ func (screen *BrowserScreen) jumpCursorUp(distance int) bool { for idx, pth := range visPaths { startJump := true for i := range pth { - if len(screen.currentPath) > i && pth[i] != screen.currentPath[i] { + if len(screen.currentPath) > i && !bytes.Equal(pth[i].name, screen.currentPath[i].name) { startJump = false } } @@ -443,7 +453,7 @@ func (screen *BrowserScreen) jumpCursorUp(distance int) bool { } isCurPath := true for i := range screen.currentPath { - if screen.currentPath[i] != findPath[i] { + if !bytes.Equal(screen.currentPath[i].name, findPath[i].name) { isCurPath = false break } @@ -462,7 +472,7 @@ func (screen *BrowserScreen) jumpCursorDown(distance int) bool { startJump := true for i := range pth { - if len(screen.currentPath) > i && pth[i] != screen.currentPath[i] { + if len(screen.currentPath) > i && !bytes.Equal(pth[i].name, screen.currentPath[i].name) { startJump = false } } @@ -476,7 +486,7 @@ func (screen *BrowserScreen) jumpCursorDown(distance int) bool { } isCurPath := true for i := range screen.currentPath { - if screen.currentPath[i] != findPath[i] { + if !bytes.Equal(screen.currentPath[i].name, findPath[i].name) { isCurPath = false break } @@ -602,26 +612,30 @@ func (screen *BrowserScreen) drawLeftPane(style Style) { func (screen *BrowserScreen) buildRightPane(style Style) { screen.rightPaneBuffer = nil - screen.niceLog = []string{} + screen.niceLog = []string{} - var logstr string - for _, v := range screen.currentPath { - logstr = fmt.Sprintf("%s / %s", logstr, stringify([]byte(v))) - } - screen.niceLog = append(screen.niceLog, logstr) + var logstr string + for _, v := range screen.currentPath { + vStr, err := v.dataType.ToString(v.name) + if err != nil { + vStr = "ERROR" + } + logstr = fmt.Sprintf("%s / %s", logstr, vStr) + } + screen.niceLog = append(screen.niceLog, logstr) b, p, err := screen.db.getGenericFromStringPath(screen.currentPath) if err == nil { if b != nil { screen.rightPaneBuffer = append(screen.rightPaneBuffer, - Line{fmt.Sprintf("Path: %s", strings.Join(stringifyPath(b.GetPath()), " → ")), style.defaultFg, style.defaultBg}) + Line{fmt.Sprintf("Path: %s", strings.Join(nodePathToStringPath(b.GetPath()), " → ")), style.defaultFg, style.defaultBg}) screen.rightPaneBuffer = append(screen.rightPaneBuffer, Line{fmt.Sprintf("Buckets: %d", len(b.buckets)), style.defaultFg, style.defaultBg}) screen.rightPaneBuffer = append(screen.rightPaneBuffer, Line{fmt.Sprintf("Pairs: %d", len(b.pairs)), style.defaultFg, style.defaultBg}) } else if p != nil { screen.rightPaneBuffer = append(screen.rightPaneBuffer, - Line{fmt.Sprintf("Path: %s", strings.Join(stringifyPath(p.GetPath()), " → ")), style.defaultFg, style.defaultBg}) + Line{fmt.Sprintf("Path: %s", strings.Join(nodePathToStringPath(p.GetPath()), " → ")), style.defaultFg, style.defaultBg}) screen.rightPaneBuffer = append(screen.rightPaneBuffer, Line{fmt.Sprintf("Key (%s): %s", p.keyDatatype.Name(), stringify([]byte(p.key))), style.defaultFg, style.defaultBg}) @@ -640,18 +654,18 @@ func (screen *BrowserScreen) buildRightPane(style Style) { } } else { screen.rightPaneBuffer = append(screen.rightPaneBuffer, - Line{fmt.Sprintf("Path: %s", strings.Join(stringifyStringPath(screen.currentPath), " → ")), style.defaultFg, style.defaultBg}) + Line{fmt.Sprintf("Path: %s", strings.Join(nodePathToStringPath(screen.currentPath), " → ")), style.defaultFg, style.defaultBg}) screen.rightPaneBuffer = append(screen.rightPaneBuffer, Line{err.Error(), termbox.ColorRed, termbox.ColorBlack}) } - screen.rightPaneBuffer = append(screen.rightPaneBuffer, - Line{"", style.defaultFg, style.defaultBg}) - screen.rightPaneBuffer = append(screen.rightPaneBuffer, - Line{"== Log ==", style.defaultBg, style.defaultFg}) - for _, v := range screen.niceLog { - screen.rightPaneBuffer = append(screen.rightPaneBuffer, - Line{v, style.defaultFg, style.defaultBg}) - } + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{"", style.defaultFg, style.defaultBg}) + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{"== Log ==", style.defaultBg, style.defaultFg}) + for _, v := range screen.niceLog { + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{v, style.defaultFg, style.defaultBg}) + } } func (screen *BrowserScreen) drawRightPane(style Style) { @@ -708,7 +722,7 @@ func formatValueJSON(val []byte) ([]byte, error) { func (screen *BrowserScreen) bucketToLines(bkt *BoltBucket, style Style) []Line { var ret []Line bfg, bbg := style.defaultFg, style.defaultBg - if comparePaths(screen.currentPath, bytePathToStringPath(bkt.GetPath())) { + if comparePaths(screen.currentPath, bkt.GetPath()) { bfg, bbg = style.cursorFg, style.cursorBg } bktPrefix := strings.Repeat(" ", len(bkt.GetPath())*2) @@ -719,7 +733,7 @@ func (screen *BrowserScreen) bucketToLines(bkt *BoltBucket, style Style) []Line } for _, bp := range bkt.pairs { pfg, pbg := style.defaultFg, style.defaultBg - if comparePaths(screen.currentPath, bytePathToStringPath(bp.GetPath())) { + if comparePaths(screen.currentPath, bp.GetPath()) { pfg, pbg = style.cursorFg, style.cursorBg } prPrefix := strings.Repeat(" ", len(bp.GetPath())*2) @@ -814,10 +828,11 @@ func (screen *BrowserScreen) startInsertItemAtParent(tp BoltType) bool { } else { var insPath string _, p, e := screen.db.getGenericFromStringPath(screen.currentPath[:len(screen.currentPath)-1]) + stringPath := nodePathToStringPath(screen.currentPath) if e == nil && p != nil { - insPath = strings.Join(screen.currentPath[:len(screen.currentPath)-2], " → ") + " → " + insPath = strings.Join(stringPath[:len(stringPath)-2], " → ") + " → " } else { - insPath = strings.Join(screen.currentPath[:len(screen.currentPath)-1], " → ") + " → " + insPath = strings.Join(stringPath[:len(stringPath)-1], " → ") + " → " } titlePrfx := "" if tp == typeBucket { @@ -857,10 +872,11 @@ func (screen *BrowserScreen) startInsertItem(tp BoltType) bool { screen.inputModal = mod var insPath string _, p, e := screen.db.getGenericFromStringPath(screen.currentPath) + strPath := nodePathToStringPath(screen.currentPath) if e == nil && p != nil { - insPath = strings.Join(screen.currentPath[:len(screen.currentPath)-1], " → ") + " → " + insPath = strings.Join(strPath[:len(strPath)-1], " → ") + " → " } else { - insPath = strings.Join(screen.currentPath, " → ") + " → " + insPath = strings.Join(strPath, " → ") + " → " } titlePrfx := "" if tp == typeBucket { @@ -901,7 +917,12 @@ func (screen *BrowserScreen) startExportValue() bool { screen.mode = modeExportValue return true } - screen.setMessage("Couldn't do string export on " + screen.currentPath[len(screen.currentPath)-1] + "(did you mean 'X'?)") + expNode := screen.currentPath[len(screen.currentPath)-1] + strVal, err := expNode.dataType.ToString(expNode.name) + if err != nil { + strVal = "ERROR" + } + screen.setMessage("Couldn't do string export on " + strVal + " (did you mean 'X'?)") return false } @@ -954,22 +975,30 @@ func (screen *BrowserScreen) refreshDatabase() { } func findNextDatatype(curr string) Datatype { - var first Datatype - var found bool - for k, v := range datatypes { - if first == nil { - first = v - } - if found { - return v - } - if k == curr { - found = true - } - } - return first + var first Datatype + var found bool + for k, v := range datatypes { + if first == nil { + first = v + } + if found { + return v + } + if k == curr { + found = true + } + } + return first } -func comparePaths(p1, p2 []string) bool { - return strings.Join(p1, " → ") == strings.Join(p2, " → ") +func comparePaths(p1, p2 []PathNode) bool { + if len(p1) != len(p2) { + return false + } + for k := range p1 { + if !bytes.Equal(p1[k].name, p2[k].name) { + return false + } + } + return true } diff --git a/stringify.go b/stringify.go index daedbba..ed95a25 100644 --- a/stringify.go +++ b/stringify.go @@ -32,11 +32,11 @@ func stringify(v []byte) string { } func stringifyStringPath(path []string) []string { - return stringifyPath(stringPathToBytePath(path)) + return stringifyPath(stringPathToBytePath(path)) } func stringifyPath(path [][]byte) []string { - var ret []string + var ret []string for _, v := range path { ret = append(ret, stringify(v)) }