diff --git a/CHANGELOG.md b/CHANGELOG.md index 69ba4b2..15e949e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Types of changes - `Added` flag `--include` (short `-i`) to only scan/dump a specific list of fields, this flag is repeatable - `Added` flag `--alias` (short `-a`) to rename fields on the fly, this flag is repeatable +- `Added` flag `--watch` (short `-w`) to the dump command - `Fixed` self reference link are no longer counted in the links counter while scanning ## [0.1.0] diff --git a/internal/app/cli/dump.go b/internal/app/cli/dump.go index 65377a1..228b304 100644 --- a/internal/app/cli/dump.go +++ b/internal/app/cli/dump.go @@ -28,7 +28,10 @@ import ( ) func NewDumpCommand(parent string, stderr *os.File, stdout *os.File, stdin *os.File) *cobra.Command { - var include []string + var ( + include []string + watch bool + ) cmd := &cobra.Command{ //nolint:exhaustruct Use: "dump path", @@ -36,13 +39,14 @@ func NewDumpCommand(parent string, stderr *os.File, stdout *os.File, stdin *os.F Example: " " + parent + " dump clients", Args: cobra.ExactArgs(1), Run: func(_ *cobra.Command, args []string) { - if err := dump(args[0], include); err != nil { + if err := dump(args[0], include, watch); err != nil { log.Fatal().Err(err).Int("return", 1).Msg("end SILO") } }, } cmd.Flags().StringSliceVarP(&include, "include", "i", []string{}, "include only these columns, exclude all others") + cmd.Flags().BoolVarP(&watch, "watch", "w", false, "watch statistics about dumped entities in stderr") cmd.Flags().SortFlags = false @@ -53,7 +57,7 @@ func NewDumpCommand(parent string, stderr *os.File, stdout *os.File, stdin *os.F return cmd } -func dump(path string, include []string) error { +func dump(path string, include []string, watch bool) error { backend, err := infra.NewBackend(path) if err != nil { return fmt.Errorf("%w", err) @@ -63,7 +67,14 @@ func dump(path string, include []string) error { driver := silo.NewDriver(backend, infra.NewDumpJSONLine(), silo.WithKeys(include)) - if err := driver.Dump(); err != nil { + if watch { + observer := infra.NewDumpObserver() + defer observer.Close() + + if err := driver.Dump(observer); err != nil { + return fmt.Errorf("%w", err) + } + } else if err := driver.Dump(); err != nil { return fmt.Errorf("%w", err) } diff --git a/internal/infra/dump_observer.go b/internal/infra/dump_observer.go new file mode 100644 index 0000000..7265094 --- /dev/null +++ b/internal/infra/dump_observer.go @@ -0,0 +1,87 @@ +// Copyright (C) 2024 CGI France +// +// This file is part of SILO. +// +// SILO is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// SILO is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SILO. If not, see . + +package infra + +import ( + "fmt" + "os" + "time" + + "github.com/cgi-fr/silo/pkg/silo" + "github.com/schollz/progressbar/v3" +) + +type DumpObserver struct { + countTotal int + countComplete int + countConsistent int + countInconsistent int + countEmpty int + bar *progressbar.ProgressBar +} + +func NewDumpObserver() *DumpObserver { + //nolint:gomnd + pgb := progressbar.NewOptions(-1, + progressbar.OptionSetDescription("Dumping ... "), + progressbar.OptionSetItsString("entity"), + progressbar.OptionSetWriter(os.Stderr), + progressbar.OptionShowIts(), + progressbar.OptionSpinnerType(11), + progressbar.OptionThrottle(time.Millisecond*10), + progressbar.OptionOnCompletion(func() { fmt.Fprintln(os.Stderr) }), + // progressbar.OptionShowDescriptionAtLineEnd(), + ) + + return &DumpObserver{ + countTotal: 0, + countComplete: 0, + countConsistent: 0, + countInconsistent: 0, + countEmpty: 0, + bar: pgb, + } +} + +func (o *DumpObserver) Entity(status silo.Status, _ map[string]int) { + o.countTotal++ + + switch status { + case silo.StatusEntityComplete: + o.countComplete++ + case silo.StatusEntityConsistent: + o.countConsistent++ + case silo.StatusEntityInconsistent: + o.countInconsistent++ + case silo.StatusEntityEmpty: + o.countEmpty++ + } + + _ = o.bar.Add(1) + + o.bar.Describe(fmt.Sprintf("Dumped %d entities / complete=%d / incomplete=%d / inconsistent=%d / empty=%d", + o.countTotal, + o.countComplete, + o.countConsistent, + o.countInconsistent, + o.countEmpty)) +} + +func (o *DumpObserver) Close() { + _ = o.bar.Close() +}