mirror of
https://codeberg.org/scip/anydb.git
synced 2025-12-17 12:31:02 +01:00
add list command, fix set command
This commit is contained in:
91
app/attr.go
Normal file
91
app/attr.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DbAttr struct {
|
||||||
|
Key string
|
||||||
|
Val string
|
||||||
|
Bin []byte
|
||||||
|
Args []string
|
||||||
|
Tags []string
|
||||||
|
File string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attr *DbAttr) ParseKV() error {
|
||||||
|
switch len(attr.Args) {
|
||||||
|
case 1:
|
||||||
|
// 1 arg = key + read from file or stdin
|
||||||
|
attr.Key = attr.Args[0]
|
||||||
|
if attr.File == "" {
|
||||||
|
attr.File = "-"
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
attr.Key = attr.Args[0]
|
||||||
|
attr.Val = attr.Args[1]
|
||||||
|
|
||||||
|
if attr.Args[1] == "-" {
|
||||||
|
attr.File = "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr.File != "" {
|
||||||
|
return attr.GetFileValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attr *DbAttr) GetFileValue() error {
|
||||||
|
var fd io.Reader
|
||||||
|
|
||||||
|
if attr.File == "-" {
|
||||||
|
stat, _ := os.Stdin.Stat()
|
||||||
|
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||||
|
fd = os.Stdin
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filehandle, err := os.OpenFile(attr.File, os.O_RDONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = filehandle
|
||||||
|
}
|
||||||
|
|
||||||
|
if fd != nil {
|
||||||
|
// read from file or stdin pipe
|
||||||
|
data, err := io.ReadAll(fd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// poor man's text file test
|
||||||
|
sdata := string(data)
|
||||||
|
if utf8.ValidString(sdata) {
|
||||||
|
attr.Val = sdata
|
||||||
|
} else {
|
||||||
|
attr.Bin = data
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// read from console stdin
|
||||||
|
var input string
|
||||||
|
var data string
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, err := fmt.Scanln(&input)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data += input + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
attr.Val = data
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
197
app/db.go
197
app/db.go
@@ -1,59 +1,206 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/asdine/storm/v3"
|
bolt "go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
Debug bool
|
Debug bool
|
||||||
DB *storm.DB
|
Dbfile string
|
||||||
}
|
DB *bolt.DB
|
||||||
|
|
||||||
type DbAttr struct {
|
|
||||||
Key string
|
|
||||||
Args []string
|
|
||||||
Tags []string
|
|
||||||
File string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DbEntry struct {
|
type DbEntry struct {
|
||||||
ID int `storm:"id,increment"`
|
Id string `json:"id"`
|
||||||
Key string `storm:"unique"`
|
Key string `json:"key"`
|
||||||
Value string `storm:"index"` // FIXME: turn info []byte or add blob?
|
Value string `json:"value"`
|
||||||
Tags []string `storm:"index"`
|
Bin []byte `json:"bin"`
|
||||||
CreatedAt time.Time `storm:"index"`
|
Tags []string `json:"tags"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DbEntries []DbEntry
|
||||||
|
|
||||||
|
type DbTag struct {
|
||||||
|
Keys []string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const BucketData string = "data"
|
||||||
|
const BucketTags string = "tags"
|
||||||
|
|
||||||
func New(file string, debug bool) (*DB, error) {
|
func New(file string, debug bool) (*DB, error) {
|
||||||
if _, err := os.Stat(filepath.Dir(file)); os.IsNotExist(err) {
|
if _, err := os.Stat(filepath.Dir(file)); os.IsNotExist(err) {
|
||||||
os.MkdirAll(filepath.Dir(file), 0700)
|
os.MkdirAll(filepath.Dir(file), 0700)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := storm.Open(file)
|
return &DB{Debug: debug, Dbfile: file}, nil
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// FIXME: defer db.Close() here leads to: Error: database not open
|
|
||||||
|
|
||||||
return &DB{Debug: debug, DB: db}, nil
|
func (db *DB) Open() error {
|
||||||
|
b, err := bolt.Open(db.Dbfile, 0600, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
db.DB = b
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Close() error {
|
func (db *DB) Close() error {
|
||||||
return db.DB.Close()
|
return db.DB.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Set(attr *DbAttr) error {
|
func (db *DB) List(attr *DbAttr) (DbEntries, error) {
|
||||||
entry := DbEntry{Key: attr.Key, Tags: attr.Tags}
|
if err := db.Open(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
var entries DbEntries
|
||||||
|
var filter *regexp.Regexp
|
||||||
|
|
||||||
if len(attr.Args) > 0 {
|
if len(attr.Args) > 0 {
|
||||||
entry.Value = attr.Args[0]
|
filter = regexp.MustCompile(attr.Args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: check attr.File or STDIN
|
err := db.DB.View(func(tx *bolt.Tx) error {
|
||||||
|
|
||||||
return db.DB.Save(&entry)
|
bucket := tx.Bucket([]byte(BucketData))
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := bucket.ForEach(func(key, jsonentry []byte) error {
|
||||||
|
var entry DbEntry
|
||||||
|
if err := json.Unmarshal(jsonentry, &entry); err != nil {
|
||||||
|
return fmt.Errorf("unable to unmarshal json: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var include bool
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case filter != nil:
|
||||||
|
if filter.MatchString(entry.Value) ||
|
||||||
|
filter.MatchString(entry.Key) ||
|
||||||
|
filter.MatchString(strings.Join(entry.Tags, " ")) {
|
||||||
|
include = true
|
||||||
|
}
|
||||||
|
case len(attr.Tags) > 0:
|
||||||
|
for _, search := range attr.Tags {
|
||||||
|
for _, tag := range entry.Tags {
|
||||||
|
if tag == search {
|
||||||
|
include = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if include {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
include = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if include {
|
||||||
|
entries = append(entries, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return entries, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Set(attr *DbAttr) error {
|
||||||
|
if err := db.Open(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
if err := attr.ParseKV(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := DbEntry{
|
||||||
|
Key: attr.Key,
|
||||||
|
Value: attr.Val,
|
||||||
|
Bin: attr.Bin,
|
||||||
|
Tags: attr.Tags,
|
||||||
|
Created: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.DB.Update(func(tx *bolt.Tx) error {
|
||||||
|
// insert data
|
||||||
|
bucket, err := tx.CreateBucketIfNotExists([]byte(BucketData))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create bucket: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonentry, err := json.Marshal(entry)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("json marshalling failure: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bucket.Put([]byte(entry.Key), []byte(jsonentry))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("insert data: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert tag, if any
|
||||||
|
// FIXME: check removed tags
|
||||||
|
if len(attr.Tags) > 0 {
|
||||||
|
bucket, err := tx.CreateBucketIfNotExists([]byte(BucketTags))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create bucket: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range entry.Tags {
|
||||||
|
dbtag := &DbTag{}
|
||||||
|
|
||||||
|
jsontag := bucket.Get([]byte(tag))
|
||||||
|
if jsontag == nil {
|
||||||
|
// the tag is empty so far, initialize it
|
||||||
|
dbtag.Keys = []string{entry.Key}
|
||||||
|
} else {
|
||||||
|
if err := json.Unmarshal(jsontag, dbtag); err != nil {
|
||||||
|
return fmt.Errorf("unable to unmarshal json: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(dbtag.Keys, entry.Key) {
|
||||||
|
// current key is not yet assigned to the tag, append it
|
||||||
|
dbtag.Keys = append(dbtag.Keys, entry.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsontag, err = json.Marshal(dbtag)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("json marshalling failure: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bucket.Put([]byte(tag), []byte(jsontag))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("insert data: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
10
app/generic.go
Normal file
10
app/generic.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
// look if a key in a map exists, generic variant
|
||||||
|
func Exists[K comparable, V any](m map[K]V, v K) bool {
|
||||||
|
if _, ok := m[v]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ var Version string = "v0.0.1"
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Debug bool
|
Debug bool
|
||||||
Dbfile string
|
Dbfile string
|
||||||
|
Mode string // wide, table, yaml, json
|
||||||
DB *app.DB
|
DB *app.DB
|
||||||
File string
|
File string
|
||||||
Tags []string
|
Tags []string
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/tlinden/anydb/app"
|
"github.com/tlinden/anydb/app"
|
||||||
"github.com/tlinden/anydb/cfg"
|
"github.com/tlinden/anydb/cfg"
|
||||||
|
"github.com/tlinden/anydb/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Set(conf *cfg.Config) *cobra.Command {
|
func Set(conf *cfg.Config) *cobra.Command {
|
||||||
@@ -52,7 +54,37 @@ func Del(conf *cfg.Config) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func List(conf *cfg.Config) *cobra.Command {
|
func List(conf *cfg.Config) *cobra.Command {
|
||||||
return nil
|
var (
|
||||||
|
attr app.DbAttr
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmd = &cobra.Command{
|
||||||
|
Use: "list [-t <tag>] [-o <mode>] [<filter-regex>]",
|
||||||
|
Short: "List database contents",
|
||||||
|
Long: `List database contents`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// errors at this stage do not cause the usage to be shown
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
attr.Args = args
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := conf.DB.List(&attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
output.List(os.Stdout, conf, entries)
|
||||||
|
|
||||||
|
return conf.DB.Close()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringVarP(&conf.Mode, "output-mode", "o", "", "output mode: wide, yaml, json, table")
|
||||||
|
cmd.PersistentFlags().StringArrayVarP(&attr.Tags, "tags", "t", nil, "tags, multiple allowed")
|
||||||
|
|
||||||
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func Find(conf *cfg.Config) *cobra.Command {
|
func Find(conf *cfg.Config) *cobra.Command {
|
||||||
|
|||||||
12
cmd/root.go
12
cmd/root.go
@@ -66,20 +66,26 @@ func Execute() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// options
|
// options
|
||||||
rootCmd.PersistentFlags().BoolVarP(&ShowVersion, "version", "v", false, "Print program version")
|
rootCmd.PersistentFlags().BoolVarP(&ShowVersion, "version", "v", false, "Print program version")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&conf.Debug, "debug", "d", false, "Enable debugging")
|
rootCmd.PersistentFlags().BoolVarP(&conf.Debug, "debug", "d", false, "Enable debugging")
|
||||||
rootCmd.PersistentFlags().StringVarP(&conf.Dbfile, "dbfile", "f", filepath.Join(os.Getenv("HOME"), ".config", "anydb", "default.db"), "DB file to use")
|
rootCmd.PersistentFlags().StringVarP(&conf.Dbfile, "dbfile", "f",
|
||||||
|
filepath.Join(home, ".config", "anydb", "default.db"), "DB file to use")
|
||||||
|
|
||||||
rootCmd.AddCommand(Set(&conf))
|
rootCmd.AddCommand(Set(&conf))
|
||||||
|
rootCmd.AddCommand(List(&conf))
|
||||||
// rootCmd.AddCommand(Set(&conf))
|
// rootCmd.AddCommand(Set(&conf))
|
||||||
// rootCmd.AddCommand(Del(&conf))
|
// rootCmd.AddCommand(Del(&conf))
|
||||||
// rootCmd.AddCommand(List(&conf))
|
|
||||||
// rootCmd.AddCommand(Find(&conf))
|
// rootCmd.AddCommand(Find(&conf))
|
||||||
// rootCmd.AddCommand(Help(&conf))
|
// rootCmd.AddCommand(Help(&conf))
|
||||||
// rootCmd.AddCommand(Man(&conf))
|
// rootCmd.AddCommand(Man(&conf))
|
||||||
|
|
||||||
err := rootCmd.Execute()
|
err = rootCmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -4,8 +4,9 @@ go 1.22.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/repr v0.4.0 // indirect
|
github.com/alecthomas/repr v0.4.0 // indirect
|
||||||
github.com/asdine/storm/v3 v3.2.1 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
github.com/spf13/cobra v1.8.1 // indirect
|
github.com/spf13/cobra v1.8.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
go.etcd.io/bbolt v1.3.11 // indirect
|
go.etcd.io/bbolt v1.3.11 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -14,6 +14,10 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
|
|||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
|
|||||||
58
output/default.go
Normal file
58
output/default.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package output
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/olekukonko/tablewriter"
|
||||||
|
"github.com/tlinden/anydb/app"
|
||||||
|
"github.com/tlinden/anydb/cfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func List(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error {
|
||||||
|
// FIXME: call sort here
|
||||||
|
// FIXME: check output mode switch to subs
|
||||||
|
|
||||||
|
tableString := &strings.Builder{}
|
||||||
|
table := tablewriter.NewWriter(tableString)
|
||||||
|
|
||||||
|
if conf.Mode == "wide" {
|
||||||
|
table.SetHeader([]string{"KEY", "VALUE", "TAGS", "TIMESTAMP"})
|
||||||
|
} else {
|
||||||
|
table.SetHeader([]string{"KEY", "VALUE"})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range entries {
|
||||||
|
if row.Value == "" {
|
||||||
|
row.Value = string(row.Bin)[0:60]
|
||||||
|
} else if len(row.Value) > 60 {
|
||||||
|
row.Value = row.Value[0:60]
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Mode == "wide" {
|
||||||
|
table.Append([]string{row.Key, row.Value, strings.Join(row.Tags, ","), row.Created.Format("02.01.2006T03:04.05")})
|
||||||
|
} else {
|
||||||
|
table.Append([]string{row.Key, row.Value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.SetAutoWrapText(false)
|
||||||
|
table.SetAutoFormatHeaders(true)
|
||||||
|
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||||
|
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||||
|
table.SetCenterSeparator("")
|
||||||
|
table.SetColumnSeparator("")
|
||||||
|
table.SetRowSeparator("")
|
||||||
|
table.SetHeaderLine(false)
|
||||||
|
table.SetBorder(false)
|
||||||
|
table.SetNoWhiteSpace(true)
|
||||||
|
|
||||||
|
table.SetTablePadding("\t") // pad with tabs
|
||||||
|
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
fmt.Fprint(writer, tableString.String())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user