summaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
authorSergey Grebenshchikov <sgreben@gmail.com>2018-03-22 02:15:42 +0100
committerSergey Grebenshchikov <sgreben@gmail.com>2018-03-22 10:04:51 +0100
commita49891bdb657d7cbb2521f305251cda1f00359d3 (patch)
treebb438378e68025641c54d0b2f2dfdcc73c33a4b0 /cmd
Initial commit1.0.0
Diffstat (limited to 'cmd')
-rw-r--r--cmd/jp/bar.go43
-rw-r--r--cmd/jp/flag.go26
-rw-r--r--cmd/jp/line.go52
-rw-r--r--cmd/jp/main.go110
-rw-r--r--cmd/jp/split.go20
5 files changed, 251 insertions, 0 deletions
diff --git a/cmd/jp/bar.go b/cmd/jp/bar.go
new file mode 100644
index 0000000..be0c230
--- /dev/null
+++ b/cmd/jp/bar.go
@@ -0,0 +1,43 @@
+package main
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/sgreben/jp/pkg/jp"
+ "github.com/sgreben/jp/pkg/jp/primitives"
+)
+
+func barPlotData(xvv, yvv [][]reflect.Value) (x []string, y []float64) {
+ for _, xv := range xvv {
+ for i := range xv {
+ x = append(x, fmt.Sprint(xv[i].Interface()))
+ }
+ }
+ for _, yv := range yvv {
+ for i := range yv {
+ yvi, ok := yv[i].Interface().(float64)
+ if ok {
+ y = append(y, yvi)
+ }
+ }
+ }
+ return
+}
+
+func barPlot(xvv, yvv [][]reflect.Value, box primitives.Box) string {
+ groups, y := barPlotData(xvv, yvv)
+ chart := jp.NewBarChart(box.Width, box.Height)
+ data := new(jp.DataTable)
+ if len(groups) != len(y) {
+ for i := range y {
+ data.AddColumn(fmt.Sprint(i))
+ }
+ } else {
+ for _, g := range groups {
+ data.AddColumn(g)
+ }
+ }
+ data.AddRow(y...)
+ return chart.Draw(data)
+}
diff --git a/cmd/jp/flag.go b/cmd/jp/flag.go
new file mode 100644
index 0000000..533b35d
--- /dev/null
+++ b/cmd/jp/flag.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+type enumVar struct {
+ Choices []string // The acceptable choices the user may pass to the flag
+ Value string // the current value of the flag
+}
+
+// Set implements the flag.Value interface.
+func (so *enumVar) Set(v string) error {
+ for _, c := range so.Choices {
+ if c == v {
+ so.Value = v
+ return nil
+ }
+ }
+ return fmt.Errorf("invalid choice; must be one of %s", strings.Join(so.Choices, ","))
+}
+
+func (so *enumVar) String() string {
+ return so.Value
+}
diff --git a/cmd/jp/line.go b/cmd/jp/line.go
new file mode 100644
index 0000000..6b75a8f
--- /dev/null
+++ b/cmd/jp/line.go
@@ -0,0 +1,52 @@
+package main
+
+import (
+ "reflect"
+
+ "github.com/sgreben/jp/pkg/jp"
+ "github.com/sgreben/jp/pkg/jp/primitives"
+)
+
+func linePlotData(xvv, yvv [][]reflect.Value) (x, y []float64) {
+ for _, xv := range xvv {
+ for i := range xv {
+ xvi, ok := xv[i].Interface().(float64)
+ if ok {
+ x = append(x, xvi)
+ }
+ }
+ }
+ for _, yv := range yvv {
+ for i := range yv {
+ yvi, ok := yv[i].Interface().(float64)
+ if ok {
+ y = append(y, yvi)
+ }
+ }
+ }
+ return
+}
+
+func linePlot(xvv, yvv [][]reflect.Value, box primitives.Box) string {
+ x, y := linePlotData(xvv, yvv)
+ chart := jp.NewLineChart(box.Width, box.Height)
+ data := new(jp.DataTable)
+ data.AddColumn("x")
+ data.AddColumn("y")
+ n := len(x)
+ if len(y) > n {
+ n = len(y)
+ }
+ // If no valid xs are given, use the indices as x values.
+ if len(x) == 0 {
+ x = make([]float64, len(y))
+ for i := 0; i < len(y); i++ {
+ x[i] = float64(i)
+ }
+ }
+ for i := 0; i < n; i++ {
+ data.AddRow(x[i%len(x)], y[i%len(y)])
+ }
+ chart.Symbol = "█"
+ return chart.Draw(data)
+}
diff --git a/cmd/jp/main.go b/cmd/jp/main.go
new file mode 100644
index 0000000..c603f68
--- /dev/null
+++ b/cmd/jp/main.go
@@ -0,0 +1,110 @@
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "reflect"
+
+ "github.com/sgreben/jp/pkg/jp/primitives"
+ "github.com/sgreben/jp/pkg/terminal"
+
+ "github.com/sgreben/jp/pkg/jsonpath"
+)
+
+type configuration struct {
+ Box primitives.Box
+ X string
+ Y string
+ XY string
+ PlotType enumVar
+}
+
+const plotTypeLine = "line"
+const plotTypeBar = "bar"
+const plotTypeScatter = "scatter"
+const plotTypeHist = "hist"
+
+var config = configuration{
+ PlotType: enumVar{
+ Value: plotTypeLine,
+ Choices: []string{
+ plotTypeLine,
+ plotTypeBar,
+ },
+ },
+}
+
+var xPattern *jsonpath.JSONPath
+var yPattern *jsonpath.JSONPath
+var xyPattern *jsonpath.JSONPath
+
+func init() {
+ flag.Var(&config.PlotType, "type", fmt.Sprintf("Plot type. One of %v", config.PlotType.Choices))
+ flag.StringVar(&config.X, "x", "", "x values (JSONPath expression)")
+ flag.StringVar(&config.Y, "y", "", "y values (JSONPath expression)")
+ flag.StringVar(&config.XY, "xy", "", "x,y value pairs (JSONPath expression). Overrides -x and -y if given.")
+ flag.IntVar(&config.Box.Width, "width", 0, "Plot width (default 0 (auto))")
+ flag.IntVar(&config.Box.Height, "height", 0, "Plot height (default 0 (auto))")
+ flag.Parse()
+ log.SetOutput(os.Stderr)
+
+ var err error
+ xPattern = jsonpath.New("x")
+ err = xPattern.Parse(fmt.Sprintf("{%s}", config.X))
+ if err != nil {
+ log.Fatal(err)
+ }
+ yPattern = jsonpath.New("y")
+ err = yPattern.Parse(fmt.Sprintf("{%s}", config.Y))
+ if err != nil {
+ log.Fatal(err)
+ }
+ if config.XY != "" {
+ xyPattern = jsonpath.New("xy")
+ err = xyPattern.Parse(fmt.Sprintf("{%s}", config.XY))
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ if config.Box.Width == 0 {
+ config.Box.Width = terminal.Width()
+ }
+ if config.Box.Height == 0 {
+ config.Box.Height = terminal.Height() - 1
+ }
+}
+
+func match(in interface{}, p *jsonpath.JSONPath) [][]reflect.Value {
+ out, err := p.FindResults(in)
+ if err != nil {
+ log.Println(err)
+ }
+ return out
+}
+
+func main() {
+ var in interface{}
+ dec := json.NewDecoder(os.Stdin)
+ err := dec.Decode(&in)
+ if err != nil {
+ log.Println(err)
+ }
+ fmt.Println()
+ var x, y [][]reflect.Value
+ if xyPattern != nil {
+ x, y = split(match(in, xyPattern))
+ } else {
+ x = match(in, xPattern)
+ y = match(in, yPattern)
+ }
+ switch config.PlotType.Value {
+ case plotTypeLine:
+ fmt.Print(linePlot(x, y, config.Box))
+ case plotTypeBar:
+ fmt.Print(barPlot(x, y, config.Box))
+ }
+
+}
diff --git a/cmd/jp/split.go b/cmd/jp/split.go
new file mode 100644
index 0000000..4f36825
--- /dev/null
+++ b/cmd/jp/split.go
@@ -0,0 +1,20 @@
+package main
+
+import "reflect"
+
+func flatten(in [][]reflect.Value) (out []reflect.Value) {
+ for _, a := range in {
+ for i := range a {
+ out = append(out, a[i])
+ }
+ }
+ return
+}
+
+func split(in [][]reflect.Value) (x, y [][]reflect.Value) {
+ flat := flatten(in)
+ n := len(flat)
+ x = [][]reflect.Value{flat[:n/2]}
+ y = [][]reflect.Value{flat[n/2:]}
+ return
+}