diff --git a/script.go b/script.go index cb5b00f..b95c14d 100644 --- a/script.go +++ b/script.go @@ -38,6 +38,7 @@ func ScriptCommands(db *DB) hive.ScriptCmdsOut { "db/lowerbound": LowerBoundCmd(db), "db/watch": WatchCmd(db), "db/initialized": InitializedCmd(db), + "db/dump": DumpCmd(db), }) } @@ -60,7 +61,7 @@ func DBCmd(db *DB) script.Cmd { "", "The individual tables can be manipulated and inspected with the", "other commands. See 'help -v db/show' etc. for detailed help.", - "Here is some examples to get you statred:", + "Here is some examples to get you started:", "", "> db/show example", "Name X", @@ -93,6 +94,46 @@ func DBCmd(db *DB) script.Cmd { ) } +func DumpCmd(db *DB) script.Cmd { + return script.Command( + script.CmdUsage{ + Summary: "Dump StateDB contents as JSON", + Flags: func(fs *pflag.FlagSet) { + fs.StringP("out", "o", "", "File to write to instead of stdout") + }, + Detail: []string{ + "The contents are written to stdout, but can be written to", + "a file instead with the -o flag.", + }, + }, + func(s *script.State, args ...string) (script.WaitFunc, error) { + file, err := s.Flags.GetString("out") + if err != nil { + return nil, err + } + + return func(s *script.State) (stdout string, stderr string, err error) { + var ( + buf strings.Builder + w io.Writer = &buf + ) + + if file != "" { + f, err := os.OpenFile(s.Path(file), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + if err != nil { + return "", "", fmt.Errorf("OpenFile(%s): %w", file, err) + } + defer f.Close() + w = f + } + + err = db.ReadTxn().WriteJSON(w) + return buf.String(), "", err + }, nil + }, + ) +} + func InitializedCmd(db *DB) script.Cmd { return script.Command( script.CmdUsage{ diff --git a/testdata/db.txtar b/testdata/db.txtar index b43ca5d..84a17a9 100644 --- a/testdata/db.txtar +++ b/testdata/db.txtar @@ -1,5 +1,5 @@ # -# This file is invoked by 'script_test.go' and tests the StateDB script commands +# This file is invoked by 'script_test.go' and tests the StateDB script commands # defined in 'script.go'. # @@ -139,6 +139,13 @@ db/cmp test1 objs.table db/lowerbound --index=id --delete test1 2 db/cmp test1 obj1.table +# Retrieve a full DB dump +db/dump +cmp stdout dump-expected.json + +db/dump -o dump-actual.json +cmp dump-actual.json dump-expected.json + # Tables db @@ -199,3 +206,12 @@ ID Tags 2 baz, foo -- empty.table -- ID Tags +-- dump-expected.json -- +{ + "test1": [ + {"ID":1,"Tags":["bar","foo"]} + ], + "test2": [ + {"ID":2,"Tags":["baz","foo"]} + ] +}