提交 5c4fa5a7 作者: Brian Tiger Chow 提交者: Juan Batiz-Benet

feat(tour) show list of topics when user tries to view topic that doesn't exist

eg.

```
ipfs2 (cmd-ref-part2) λ. go build ./...; ./ipfs2 tour 0

Tour 0 - Hello Mars

Hello Mars

ipfs2 (cmd-ref-part2) λ. go build ./...; ./ipfs2 tour 10

ERROR
    no topic with id: 10

TOPICS

    0 - Hello Mars
    0.1 - Hello Mars 2

```
上级 afa5eedb
......@@ -11,8 +11,12 @@ import (
config "github.com/jbenet/go-ipfs/config"
internal "github.com/jbenet/go-ipfs/core/commands2/internal"
tour "github.com/jbenet/go-ipfs/tour"
"github.com/jbenet/go-ipfs/util"
)
// TODO the parent function now uses tourOutput. Migrate the children to also
// use the tourOutput struct
var tourCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "An introduction to IPFS",
......@@ -33,38 +37,101 @@ IPFS very quickly. To start, run:
"next": cmdIpfsTourNext,
"restart": cmdIpfsTourRestart,
},
Run: func(req cmds.Request) (interface{}, error) {
Run: tourRunFunc,
Marshalers: cmds.MarshalerMap{
cmds.Text: tourTextMarshaler,
},
Type: &tourOutput{},
}
out := new(bytes.Buffer)
cfg, err := req.Context().GetConfig()
if err != nil {
return nil, err
}
// tourOutput is a union type. It either contains a Topic or it contains the
// list of Topics and an Error.
type tourOutput struct {
Topic *tour.Topic
strs, err := internal.CastToStrings(req.Arguments())
if err != nil {
return nil, err
}
Topics []tour.Topic
Error error
}
id := tour.TopicID(cfg.Tour.Last)
if len(strs) > 0 {
id = tour.TopicID(strs[0])
}
func tourTextMarshaler(r cmds.Response) ([]byte, error) {
output, ok := r.Output().(*tourOutput)
if !ok {
return nil, util.ErrCast()
}
// can be listing when error
var buf bytes.Buffer
err := printTourOutput(&buf, output)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
t, err := tourGet(id)
if err != nil {
return nil, cmds.ClientError(err.Error())
}
func printTourOutput(w io.Writer, output *tourOutput) error {
tmpl := `{{ if .Error }}
ERROR
{{ .Error }}
TOPICS
{{ range $topic := .Topics }}
{{ $topic.ID }} - {{ $topic.Title }} {{ end }}
{{ else if .Topic }}
Tour {{ .Topic.ID }} - {{ .Topic.Title }}
err = tourShow(out, t)
if err != nil {
return nil, err
{{ .Topic.Text }}
{{ end }}
`
tourTmpl, err := template.New("tour").Parse(tmpl)
if err != nil {
return err
}
return tourTmpl.Execute(w, output)
}
func tourRunFunc(req cmds.Request) (interface{}, error) {
cfg, err := req.Context().GetConfig()
if err != nil {
return nil, err
}
strs, err := internal.CastToStrings(req.Arguments())
if err != nil {
return nil, err
}
id := tour.TopicID(cfg.Tour.Last)
if len(strs) > 0 {
id = tour.TopicID(strs[0])
}
t, err := tourGet(id)
if err != nil {
// If no topic exists for this id, we handle this error right here.
// To help the user achieve the task, we construct a response
// comprised of...
// 1) a simple error message
// 2) the full list of topics
output := &tourOutput{
Error: err,
}
for _, id := range tour.IDs {
t, ok := tour.Topics[id]
if !ok {
return nil, err
}
output.Topics = append(output.Topics, t)
}
return out, nil
},
return output, nil
// return nil, cmds.ClientError(err.Error())
}
return &tourOutput{Topic: t}, nil
}
// TODO use tourOutput like parent command
var cmdIpfsTourNext = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Show the next IPFS Tour topic",
......@@ -97,7 +164,7 @@ var cmdIpfsTourNext = &cmds.Command{
}
w.WriteTo(os.Stdout) // TODO write to res.SetValue
return nil, nil
return w, nil
},
}
......@@ -122,6 +189,7 @@ var cmdIpfsTourRestart = &cmds.Command{
},
}
// TODO use tourOutput like parent command
var cmdIpfsTourList = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Show a list of IPFS Tour topics",
......@@ -171,6 +239,7 @@ Tour {{ .ID }} - {{ .Title }}
return ttempl.Execute(w, t)
}
// tourGet returns an error if topic does not exist
func tourGet(id tour.ID) (*tour.Topic, error) {
t, found := tour.Topics[id]
if !found {
......
......@@ -2,6 +2,7 @@ package main
import (
"bytes"
"errors"
"testing"
"github.com/jbenet/go-ipfs/tour"
......@@ -11,8 +12,9 @@ func TestParseTourTemplate(t *testing.T) {
topic := &tour.Topic{
ID: "42",
Title: "IPFS CLI test files",
Text: `Welcome to the IPFS test files
This is where we test our beautiful command line interfaces
Text: `
Welcome to the IPFS test files
This is where we test our beautiful command line interfaces
`,
}
var buf bytes.Buffer
......@@ -22,3 +24,48 @@ func TestParseTourTemplate(t *testing.T) {
}
t.Log(buf.String())
}
func TestRenderTourOutputList(t *testing.T) {
t.Log(`Ensure we can successfully print the tour output when there's an
error and list of tour topics`)
listOutput := &tourOutput{
Error: errors.New("Topic 42 does not exist"),
Topics: []tour.Topic{
tour.Topic{
ID: "41",
Title: "Being one shy of the mark",
Text: "Poor thing.",
},
tour.Topic{
ID: "44",
Title: "Two shy of the mark",
Text: "Oh no.",
},
},
}
var list bytes.Buffer
if err := printTourOutput(&list, listOutput); err != nil {
t.Fatal(err)
}
t.Log(list.String())
}
func TestRenderTourOutputSingle(t *testing.T) {
t.Log(`
When there's just a single topic in the output, ensure we can render the
template`)
singleOutput := &tourOutput{
Topic: &tour.Topic{
ID: "42",
Title: "Informative!",
Text: "Compelling!",
},
}
var single bytes.Buffer
if err := printTourOutput(&single, singleOutput); err != nil {
t.Fatal(err)
}
t.Log(single.String())
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论