Skip to content

Commit

Permalink
Added examples to 'help' output in REPL
Browse files Browse the repository at this point in the history
Fixes #151
  • Loading branch information
tsandall committed Nov 25, 2016
1 parent 002109f commit e83a083
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 32 deletions.
140 changes: 113 additions & 27 deletions repl/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
package repl

import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"io"
"os"
"sort"
Expand All @@ -20,6 +22,7 @@ import (
"github.com/open-policy-agent/opa/storage"
"github.com/open-policy-agent/opa/topdown"
"github.com/open-policy-agent/opa/topdown/explain"
"github.com/open-policy-agent/opa/version"

"github.com/peterh/liner"
)
Expand Down Expand Up @@ -53,19 +56,17 @@ const (
explainTruth explainMode = iota
)

const (
defaultREPLModuleID = "repl"
)

// New returns a new instance of the REPL.
func New(store *storage.Storage, historyPath string, output io.Writer, outputFormat string, banner string) *REPL {
mod := defaultModule()
moduleID := mod.Package.Path.String()

module := defaultModule()
moduleID := module.Package.Path.String()

return &REPL{
output: output,
store: store,
modules: map[string]*ast.Module{
moduleID: mod,
moduleID: module,
},
currentModuleID: moduleID,
outputFormat: outputFormat,
Expand All @@ -77,8 +78,49 @@ func New(store *storage.Storage, historyPath string, output io.Writer, outputFor
}
}

const (
defaultREPLModuleID = "repl"
)

func defaultModule() *ast.Module {
return ast.MustParseModule(fmt.Sprint("package ", defaultREPLModuleID))

module := `
package {{.ModuleID}}
version = {
"Version": "{{.Version}}",
"BuildCommit": "{{.BuildCommit}}",
"BuildTimestamp": "{{.BuildTimestamp}}",
"BuildHostname": "{{.BuildHostname}}"
}
`

tmpl, err := template.New("").Parse(module)
if err != nil {
panic(err)
}

var buf bytes.Buffer

err = tmpl.Execute(&buf, struct {
ModuleID string
Version string
BuildCommit string
BuildTimestamp string
BuildHostname string
}{
ModuleID: defaultREPLModuleID,
Version: version.Version,
BuildCommit: version.Vcs,
BuildTimestamp: version.Timestamp,
BuildHostname: version.Hostname,
})

if err != nil {
panic(err)
}

return ast.MustParseModule(buf.String())
}

// Loop will run until the user enters "exit", Ctrl+C, Ctrl+D, or an unexpected error occurs.
Expand Down Expand Up @@ -246,25 +288,9 @@ func (r *REPL) cmdFormat(s string) bool {
}

func (r *REPL) cmdHelp() bool {

all := extra[:]
all = append(all, builtin[:]...)

maxLength := 0

for _, c := range all {
length := len(c.syntax())
if length > maxLength {
maxLength = length
}
}

f := fmt.Sprintf("%%%dv : %%v\n", maxLength)

for _, c := range all {
fmt.Printf(f, c.syntax(), c.help)
}

fmt.Fprintln(r.output, "")
printHelpExamples(r.output, r.initPrompt)
printHelpCommands(r.output)
return false
}

Expand Down Expand Up @@ -924,6 +950,17 @@ func (c commandDesc) syntax() string {
return c.name
}

type exampleDesc struct {
example string
comment string
}

var examples = [...]exampleDesc{
{"data", "show all documents"},
{"data[x] = _", "show all top level keys"},
{"data.repl.version", "drill into specific document"},
}

var extra = [...]commandDesc{
{"<stmt>", []string{}, "evaluate the statement"},
{"package", []string{"<term>"}, "change active package"},
Expand Down Expand Up @@ -1080,3 +1117,52 @@ func mangleEvent(store *storage.Storage, txn storage.Transaction, event *topdown
}
return nil
}

func printHelpExamples(output io.Writer, promptSymbol string) {

fmt.Fprintln(output, "Examples")
fmt.Fprintln(output, "========")
fmt.Fprintln(output, "")

maxLength := 0
for _, ex := range examples {
if len(ex.example) > maxLength {
maxLength = len(ex.example)
}
}

f := fmt.Sprintf("%v%%-%dv # %%v\n", promptSymbol, maxLength+1)

for _, ex := range examples {
fmt.Fprintf(output, f, ex.example, ex.comment)
}

fmt.Fprintln(output, "")
}

func printHelpCommands(output io.Writer) {

fmt.Fprintln(output, "Commands")
fmt.Fprintln(output, "========")
fmt.Fprintln(output, "")

all := extra[:]
all = append(all, builtin[:]...)

maxLength := 0

for _, c := range all {
length := len(c.syntax())
if length > maxLength {
maxLength = length
}
}

f := fmt.Sprintf("%%%dv : %%v\n", maxLength)

for _, c := range all {
fmt.Fprintf(output, f, c.syntax(), c.help)
}

fmt.Fprintln(output, "")
}
17 changes: 12 additions & 5 deletions repl/repl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func TestComplete(t *testing.T) {
"data.a.b.c.q",
"data.a.b.d.r",
"data.repl.s",
"data.repl.version",
}

sort.Strings(result)
Expand Down Expand Up @@ -139,14 +140,15 @@ func TestShow(t *testing.T) {
var buffer bytes.Buffer
repl := newRepl(store, &buffer)

repl.OneShot("package repl_test")
repl.OneShot("show")
assertREPLText(t, buffer, "package repl\n")
assertREPLText(t, buffer, "package repl_test\n")
buffer.Reset()

repl.OneShot("import xyz")
repl.OneShot("show")

expected := `package repl
expected := `package repl_test
import xyz` + "\n"
assertREPLText(t, buffer, expected)
Expand All @@ -155,7 +157,7 @@ import xyz` + "\n"
repl.OneShot("import data.foo as bar")
repl.OneShot("show")

expected = `package repl
expected = `package repl_test
import xyz
import data.foo as bar` + "\n"
Expand All @@ -166,7 +168,7 @@ import data.foo as bar` + "\n"
repl.OneShot("p[2] :- true")
repl.OneShot("show")

expected = `package repl
expected = `package repl_test
import xyz
import data.foo as bar
Expand All @@ -182,7 +184,7 @@ p[2] :- true` + "\n"
assertREPLText(t, buffer, "package abc\n")
buffer.Reset()

repl.OneShot("package repl")
repl.OneShot("package repl_test")
repl.OneShot("show")

assertREPLText(t, buffer, expected)
Expand Down Expand Up @@ -397,6 +399,11 @@ func TestEvalData(t *testing.T) {
}
}`)
result := parseJSON(buffer.String())

// Strip REPL documents out as these change depending on build settings.
data := result.(map[string]interface{})
delete(data, "repl")

if !reflect.DeepEqual(result, expected) {
t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, result)
}
Expand Down

0 comments on commit e83a083

Please sign in to comment.