package commands import ( "bytes" "fmt" "html/template" "io" cmds "github.com/ipfs/go-ipfs/commands" config "github.com/ipfs/go-ipfs/repo/config" fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" tour "github.com/ipfs/go-ipfs/tour" ) var tourCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "An introduction to IPFS.", ShortDescription: ` This is a tour that takes you through various IPFS concepts, features, and tools to make sure you get up to speed with IPFS very quickly. To start, run: ipfs tour `, }, Arguments: []cmds.Argument{ cmds.StringArg("id", false, false, "The id of the topic you would like to tour."), }, Subcommands: map[string]*cmds.Command{ "list": cmdIpfsTourList, "next": cmdIpfsTourNext, "restart": cmdIpfsTourRestart, }, Run: tourRunFunc, } func tourRunFunc(req cmds.Request, res cmds.Response) { cfg, err := req.InvocContext().GetConfig() if err != nil { res.SetError(err, cmds.ErrNormal) return } id := tour.TopicID(cfg.Tour.Last) if len(req.Arguments()) > 0 { id = tour.TopicID(req.Arguments()[0]) } w := new(bytes.Buffer) 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 fmt.Fprintln(w, "ERROR") fmt.Fprintln(w, err) fmt.Fprintln(w, "") fprintTourList(w, tour.TopicID(cfg.Tour.Last)) res.SetOutput(w) return } fprintTourShow(w, t) res.SetOutput(w) } var cmdIpfsTourNext = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Show the next IPFS Tour topic.", }, Run: func(req cmds.Request, res cmds.Response) { w := new(bytes.Buffer) path := req.InvocContext().ConfigRoot cfg, err := req.InvocContext().GetConfig() if err != nil { res.SetError(err, cmds.ErrNormal) return } id := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) topic, err := tourGet(id) if err != nil { res.SetError(err, cmds.ErrNormal) return } if err := fprintTourShow(w, topic); err != nil { res.SetError(err, cmds.ErrNormal) return } // topic changed, not last. write it out. if string(id) != cfg.Tour.Last { cfg.Tour.Last = string(id) err := writeConfig(path, cfg) if err != nil { res.SetError(err, cmds.ErrNormal) return } } res.SetOutput(w) }, } var cmdIpfsTourRestart = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Restart the IPFS Tour.", }, Run: func(req cmds.Request, res cmds.Response) { path := req.InvocContext().ConfigRoot cfg, err := req.InvocContext().GetConfig() if err != nil { res.SetError(err, cmds.ErrNormal) return } cfg.Tour.Last = "" err = writeConfig(path, cfg) if err != nil { res.SetError(err, cmds.ErrNormal) return } }, } var cmdIpfsTourList = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Show a list of IPFS Tour topics.", }, Run: func(req cmds.Request, res cmds.Response) { cfg, err := req.InvocContext().GetConfig() if err != nil { res.SetError(err, cmds.ErrNormal) return } w := new(bytes.Buffer) fprintTourList(w, tour.TopicID(cfg.Tour.Last)) res.SetOutput(w) }, } func fprintTourList(w io.Writer, lastid tour.ID) { for _, id := range tour.IDs { c := ' ' switch { case id == lastid: c = '*' case id.LessThan(lastid): c = '✓' } t := tour.Topics[id] fmt.Fprintf(w, "- %c %-5.5s %s\n", c, id, t.Title) } } // fprintTourShow writes a text-formatted topic to the writer func fprintTourShow(w io.Writer, t *tour.Topic) error { tmpl := ` Tour {{ .ID }} - {{ .Title }} {{ .Text }} ` ttempl, err := template.New("tour").Parse(tmpl) if err != nil { return err } return ttempl.Execute(w, t) } // tourGet returns the topic given its ID. Returns an error if topic does not // exist. func tourGet(id tour.ID) (*tour.Topic, error) { t, found := tour.Topics[id] if !found { return nil, fmt.Errorf("no topic with id: %s", id) } return &t, nil } // TODO share func func writeConfig(path string, cfg *config.Config) error { // NB: This needs to run on the daemon. r, err := fsrepo.Open(path) if err != nil { return err } defer r.Close() return r.SetConfig(cfg) }