From 6f7bab389e4a52ed8bf03861c15ab42541791568 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 26 Oct 2014 08:32:08 -0700 Subject: [PATCH 1/2] tour: command --- cmd/ipfs/bootstrap.go | 15 ----- cmd/ipfs/config.go | 22 ++++++- cmd/ipfs/ipfs.go | 1 + cmd/ipfs/tour.go | 134 ++++++++++++++++++++++++++++++++++++++++++ config/config.go | 7 +++ tour/all.go | 26 ++++++++ tour/tour.go | 86 +++++++++++++++++++++++++++ 7 files changed, 273 insertions(+), 18 deletions(-) create mode 100644 cmd/ipfs/tour.go create mode 100644 tour/all.go create mode 100644 tour/tour.go diff --git a/cmd/ipfs/bootstrap.go b/cmd/ipfs/bootstrap.go index 68a1ad633..6f6a78574 100644 --- a/cmd/ipfs/bootstrap.go +++ b/cmd/ipfs/bootstrap.go @@ -187,21 +187,6 @@ func bootstrapListCmd(c *commander.Command, inp []string) error { return nil } -func writeConfig(c *commander.Command, cfg *config.Config) error { - - confdir, err := getConfigDir(c) - if err != nil { - return err - } - - filename, err := config.Filename(confdir) - if err != nil { - return err - } - - return config.WriteConfigFile(filename, cfg) -} - func bootstrapInputToPeers(input []string) ([]*config.BootstrapPeer, error) { split := func(addr string) (string, string) { idx := strings.LastIndex(addr, "/") diff --git a/cmd/ipfs/config.go b/cmd/ipfs/config.go index 5f600cac8..f1f8be92e 100644 --- a/cmd/ipfs/config.go +++ b/cmd/ipfs/config.go @@ -3,13 +3,14 @@ package main import ( "errors" "fmt" + "io" + "os" + "os/exec" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" config "github.com/jbenet/go-ipfs/config" u "github.com/jbenet/go-ipfs/util" - "io" - "os" - "os/exec" ) var cmdIpfsConfig = &commander.Command{ @@ -125,3 +126,18 @@ func configEditor(filename string) error { cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr return cmd.Run() } + +func writeConfig(c *commander.Command, cfg *config.Config) error { + + confdir, err := getConfigDir(c) + if err != nil { + return err + } + + filename, err := config.Filename(confdir) + if err != nil { + return err + } + + return config.WriteConfigFile(filename, cfg) +} diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 42166f5a4..9ce5d7faf 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -73,6 +73,7 @@ Use "ipfs help " for more information about a command. cmdIpfsUpdate, cmdIpfsLog, cmdIpfsPin, + cmdIpfsTour, }, Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError), } diff --git a/cmd/ipfs/tour.go b/cmd/ipfs/tour.go new file mode 100644 index 000000000..305cd37cd --- /dev/null +++ b/cmd/ipfs/tour.go @@ -0,0 +1,134 @@ +// +build linux darwin freebsd + +package main + +import ( + "fmt" + + config "github.com/jbenet/go-ipfs/config" + tour "github.com/jbenet/go-ipfs/tour" + + commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" +) + +var cmdIpfsTour = &commander.Command{ + UsageLine: "tour []", + Short: "Take the IPFS Tour.", + Long: `ipfs tour - Take the IPFS Tour. + + ipfs tour [] - Show tour topic. Default to current. + ipfs tour next - Show the next tour topic. + ipfs tour list - Show a list of topics. + ipfs tour restart - Restart the tour. + +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 +`, + Run: tourCmd, + Subcommands: []*commander.Command{ + cmdIpfsTourNext, + cmdIpfsTourList, + cmdIpfsTourRestart, + }, +} + +var cmdIpfsTourNext = &commander.Command{ + UsageLine: "next", + Short: "Show the next IPFS Tour topic.", + Run: tourNextCmd, +} + +var cmdIpfsTourList = &commander.Command{ + UsageLine: "list", + Short: "Show a list of IPFS Tour topics.", + Run: tourListCmd, +} + +var cmdIpfsTourRestart = &commander.Command{ + UsageLine: "restart", + Short: "Restart the IPFS Tour.", + Run: tourRestartCmd, +} + +func tourCmd(c *commander.Command, inp []string) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + topic := tour.TopicID(cfg.Tour.Last) + if len(inp) > 0 { + topic = tour.TopicID(inp[0]) + } + return tourShow(topic) +} + +func tourNextCmd(c *commander.Command, _ []string) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + topic := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) + if err := tourShow(topic); err != nil { + return err + } + + // if topic didn't change (last) done + if string(topic) == cfg.Tour.Last { + return nil + } + + // topic changed, not last. write it out. + cfg.Tour.Last = string(topic) + return writeConfig(c, cfg) +} + +func tourListCmd(c *commander.Command, _ []string) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + lastid := tour.TopicID(cfg.Tour.Last) + + for _, id := range tour.IDs { + c := ' ' + switch { + case id == lastid: + c = '*' + case id.LessThan(lastid): + c = '✓' + } + + t := tour.Topics[id] + fmt.Printf("- %c %5.5s %s\n", c, id, t.Title) + } + return nil +} + +func tourRestartCmd(c *commander.Command, _ []string) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + cfg.Tour.Last = "" + return writeConfig(c, cfg) +} + +func tourShow(id tour.ID) error { + t, found := tour.Topics[id] + if !found { + return fmt.Errorf("no topic with id: %s", id) + } + + fmt.Printf("Tour %s - %s\n\n%s\n", t.ID, t.Title, t.Text) + return nil +} + +func lastTour(cfg *config.Config) string { + return "" +} diff --git a/config/config.go b/config/config.go index b04567bdd..34133d708 100644 --- a/config/config.go +++ b/config/config.go @@ -47,6 +47,12 @@ func (bp *BootstrapPeer) String() string { return bp.Address + "/" + bp.PeerID } +// Tour stores the ipfs tour read-list and resume point +type Tour struct { + Last string // last tour topic read + // Done []string // all topics done so far +} + // Config is used to load IPFS config files. type Config struct { Identity Identity // local node's peer identity @@ -55,6 +61,7 @@ type Config struct { Mounts Mounts // local node's mount points Version Version // local node's version management Bootstrap []*BootstrapPeer // local nodes's bootstrap peers + Tour Tour // local node's tour position } // DefaultPathRoot is the path to the default config dir location. diff --git a/tour/all.go b/tour/all.go new file mode 100644 index 000000000..50a9b0922 --- /dev/null +++ b/tour/all.go @@ -0,0 +1,26 @@ +package tour + +import "sort" + +func init() { + for _, t := range allTopics { + Topics[t.ID] = t + IDs = append(IDs, t.ID) + } + + sort.Sort(IDSlice(IDs)) +} + +// Topics contains a mapping of Tour Topic ID to Topic +var allTopics = []Topic{ + Topic{ + ID: ID("0"), + Title: "Hello Mars", + Text: "Hello Mars", + }, + Topic{ + ID: ID("0.1"), + Title: "Hello Mars 2", + Text: "Hello Mars 2", + }, +} diff --git a/tour/tour.go b/tour/tour.go new file mode 100644 index 000000000..f983293b5 --- /dev/null +++ b/tour/tour.go @@ -0,0 +1,86 @@ +package tour + +import ( + "strconv" + "strings" + + u "github.com/jbenet/go-ipfs/util" +) + +var log = u.Logger("tour") + +// ID is a string identifier for topics +type ID string + +// LessThan returns whether this ID is sorted earlier than another. +func (i ID) LessThan(o ID) bool { + return compareDottedInts(string(i), string(o)) +} + +// IDSlice implements the sort interface for ID slices. +type IDSlice []ID + +func (a IDSlice) Len() int { return len(a) } +func (a IDSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a IDSlice) Less(i, j int) bool { return a[i].LessThan(a[j]) } + +// Topic is a type of objects that structures a tour topic. +type Topic struct { + ID ID + Title string + Text string +} + +// Topics is a sorted list of topic IDs +var IDs []ID + +// Topics contains a mapping of Tour Topic ID to Topic +var Topics = map[ID]Topic{} + +// NextTopic returns the next in-order topic +func NextTopic(topic ID) ID { + for _, id := range IDs { + if topic.LessThan(id) { + return id + } + } + return topic // last one, it seems. +} + +// TopicID returns a valid tour topic ID from given string +func TopicID(t string) ID { + if t == "" { // if empty, use first ID + return IDs[0] + } + return ID(t) +} + +func compareDottedInts(i, o string) bool { + is := strings.Split(i, ".") + os := strings.Split(o, ".") + + for n, vis := range is { + if n >= len(os) { + return false // os is smaller. + } + + vos := os[n] + ivis, err1 := strconv.Atoi(vis) + ivos, err2 := strconv.Atoi(vos) + if err1 != nil || err2 != nil { + log.Error(err1) + log.Error(err2) + panic("tour ID LessThan: not an int") + } + + if ivis < ivos { + return true + } + + if ivis > ivos { + return false + } + } + + return len(os) > len(is) +} From 0ee4b3ec778fd6c164769bd0b87e19f44f0a8e8b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 26 Oct 2014 10:03:38 -0700 Subject: [PATCH 2/2] tour list: left justify --- cmd/ipfs/tour.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs/tour.go b/cmd/ipfs/tour.go index 305cd37cd..0656625e8 100644 --- a/cmd/ipfs/tour.go +++ b/cmd/ipfs/tour.go @@ -104,7 +104,7 @@ func tourListCmd(c *commander.Command, _ []string) error { } t := tour.Topics[id] - fmt.Printf("- %c %5.5s %s\n", c, id, t.Title) + fmt.Printf("- %c %-5.5s %s\n", c, id, t.Title) } return nil }