提交 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 ( ...@@ -11,8 +11,12 @@ import (
config "github.com/jbenet/go-ipfs/config" config "github.com/jbenet/go-ipfs/config"
internal "github.com/jbenet/go-ipfs/core/commands2/internal" internal "github.com/jbenet/go-ipfs/core/commands2/internal"
tour "github.com/jbenet/go-ipfs/tour" 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{ var tourCmd = &cmds.Command{
Helptext: cmds.HelpText{ Helptext: cmds.HelpText{
Tagline: "An introduction to IPFS", Tagline: "An introduction to IPFS",
...@@ -33,38 +37,101 @@ IPFS very quickly. To start, run: ...@@ -33,38 +37,101 @@ IPFS very quickly. To start, run:
"next": cmdIpfsTourNext, "next": cmdIpfsTourNext,
"restart": cmdIpfsTourRestart, "restart": cmdIpfsTourRestart,
}, },
Run: func(req cmds.Request) (interface{}, error) { Run: tourRunFunc,
Marshalers: cmds.MarshalerMap{
cmds.Text: tourTextMarshaler,
},
Type: &tourOutput{},
}
out := new(bytes.Buffer) // tourOutput is a union type. It either contains a Topic or it contains the
cfg, err := req.Context().GetConfig() // list of Topics and an Error.
if err != nil { type tourOutput struct {
return nil, err Topic *tour.Topic
}
strs, err := internal.CastToStrings(req.Arguments()) Topics []tour.Topic
if err != nil { Error error
return nil, err }
}
id := tour.TopicID(cfg.Tour.Last) func tourTextMarshaler(r cmds.Response) ([]byte, error) {
if len(strs) > 0 { output, ok := r.Output().(*tourOutput)
id = tour.TopicID(strs[0]) 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) func printTourOutput(w io.Writer, output *tourOutput) error {
if err != nil { tmpl := `{{ if .Error }}
return nil, cmds.ClientError(err.Error()) ERROR
} {{ .Error }}
TOPICS
{{ range $topic := .Topics }}
{{ $topic.ID }} - {{ $topic.Title }} {{ end }}
{{ else if .Topic }}
Tour {{ .Topic.ID }} - {{ .Topic.Title }}
err = tourShow(out, t) {{ .Topic.Text }}
if err != nil { {{ end }}
return nil, err `
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{ var cmdIpfsTourNext = &cmds.Command{
Helptext: cmds.HelpText{ Helptext: cmds.HelpText{
Tagline: "Show the next IPFS Tour topic", Tagline: "Show the next IPFS Tour topic",
...@@ -97,7 +164,7 @@ var cmdIpfsTourNext = &cmds.Command{ ...@@ -97,7 +164,7 @@ var cmdIpfsTourNext = &cmds.Command{
} }
w.WriteTo(os.Stdout) // TODO write to res.SetValue w.WriteTo(os.Stdout) // TODO write to res.SetValue
return nil, nil return w, nil
}, },
} }
...@@ -122,6 +189,7 @@ var cmdIpfsTourRestart = &cmds.Command{ ...@@ -122,6 +189,7 @@ var cmdIpfsTourRestart = &cmds.Command{
}, },
} }
// TODO use tourOutput like parent command
var cmdIpfsTourList = &cmds.Command{ var cmdIpfsTourList = &cmds.Command{
Helptext: cmds.HelpText{ Helptext: cmds.HelpText{
Tagline: "Show a list of IPFS Tour topics", Tagline: "Show a list of IPFS Tour topics",
...@@ -171,6 +239,7 @@ Tour {{ .ID }} - {{ .Title }} ...@@ -171,6 +239,7 @@ Tour {{ .ID }} - {{ .Title }}
return ttempl.Execute(w, t) return ttempl.Execute(w, t)
} }
// tourGet returns an error if topic does not exist
func tourGet(id tour.ID) (*tour.Topic, error) { func tourGet(id tour.ID) (*tour.Topic, error) {
t, found := tour.Topics[id] t, found := tour.Topics[id]
if !found { if !found {
......
...@@ -2,6 +2,7 @@ package main ...@@ -2,6 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"errors"
"testing" "testing"
"github.com/jbenet/go-ipfs/tour" "github.com/jbenet/go-ipfs/tour"
...@@ -11,8 +12,9 @@ func TestParseTourTemplate(t *testing.T) { ...@@ -11,8 +12,9 @@ func TestParseTourTemplate(t *testing.T) {
topic := &tour.Topic{ topic := &tour.Topic{
ID: "42", ID: "42",
Title: "IPFS CLI test files", Title: "IPFS CLI test files",
Text: `Welcome to the IPFS test files Text: `
This is where we test our beautiful command line interfaces Welcome to the IPFS test files
This is where we test our beautiful command line interfaces
`, `,
} }
var buf bytes.Buffer var buf bytes.Buffer
...@@ -22,3 +24,48 @@ func TestParseTourTemplate(t *testing.T) { ...@@ -22,3 +24,48 @@ func TestParseTourTemplate(t *testing.T) {
} }
t.Log(buf.String()) 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论