tour: command
Juan Batiz-Benet committed
Oct 26, 2014 at 08:32 UTC
6f7bab389e4a52ed8bf03861c15ab42541791568
7 files changed
+273
-18
cmd/ipfs/bootstrap.go
-15
@@ -187,21 +187,6 @@ func bootstrapListCmd(c *commander.Command, inp []string) error {
187
return nil
188
}
189
190
-func writeConfig(c *commander.Command, cfg *config.Config) error {
191
-
192
- confdir, err := getConfigDir(c)
193
- if err != nil {
194
- return err
195
- }
196
-
197
- filename, err := config.Filename(confdir)
198
- if err != nil {
199
- return err
200
- }
201
-
202
- return config.WriteConfigFile(filename, cfg)
203
-}
204
-
190
func bootstrapInputToPeers(input []string) ([]*config.BootstrapPeer, error) {
191
split := func(addr string) (string, string) {
192
idx := strings.LastIndex(addr, "/")
cmd/ipfs/config.go
+19
-3
@@ -3,13 +3,14 @@ package main
3
import (
4
"errors"
5
"fmt"
6
+ "io"
7
+ "os"
8
+ "os/exec"
9
+
10
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag"
11
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander"
12
config "github.com/jbenet/go-ipfs/config"
13
u "github.com/jbenet/go-ipfs/util"
10
- "io"
11
- "os"
12
- "os/exec"
14
)
15
16
var cmdIpfsConfig = &commander.Command{
@@ -125,3 +126,18 @@ func configEditor(filename string) error {
126
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
127
return cmd.Run()
128
}
129
+
130
+func writeConfig(c *commander.Command, cfg *config.Config) error {
131
+
132
+ confdir, err := getConfigDir(c)
133
+ if err != nil {
134
+ return err
135
+ }
136
+
137
+ filename, err := config.Filename(confdir)
138
+ if err != nil {
139
+ return err
140
+ }
141
+
142
+ return config.WriteConfigFile(filename, cfg)
143
+}
cmd/ipfs/ipfs.go
+1
@@ -73,6 +73,7 @@ Use "ipfs help <command>" for more information about a command.
73
cmdIpfsUpdate,
74
cmdIpfsLog,
75
cmdIpfsPin,
76
+ cmdIpfsTour,
77
},
78
Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
79
}
cmd/ipfs/tour.go
new
+134
@@ -0,0 +1,134 @@
1
+// +build linux darwin freebsd
2
+
3
+package main
4
+
5
+import (
6
+ "fmt"
7
+
8
+ config "github.com/jbenet/go-ipfs/config"
9
+ tour "github.com/jbenet/go-ipfs/tour"
10
+
11
+ commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander"
12
+)
13
+
14
+var cmdIpfsTour = &commander.Command{
15
+ UsageLine: "tour [<number>]",
16
+ Short: "Take the IPFS Tour.",
17
+ Long: `ipfs tour - Take the IPFS Tour.
18
+
19
+ ipfs tour [<number>] - Show tour topic. Default to current.
20
+ ipfs tour next - Show the next tour topic.
21
+ ipfs tour list - Show a list of topics.
22
+ ipfs tour restart - Restart the tour.
23
+
24
+This is a tour that takes you through various IPFS concepts,
25
+features, and tools to make sure you get up to speed with
26
+IPFS very quickly. To start, run:
27
+
28
+ ipfs tour
29
+`,
30
+ Run: tourCmd,
31
+ Subcommands: []*commander.Command{
32
+ cmdIpfsTourNext,
33
+ cmdIpfsTourList,
34
+ cmdIpfsTourRestart,
35
+ },
36
+}
37
+
38
+var cmdIpfsTourNext = &commander.Command{
39
+ UsageLine: "next",
40
+ Short: "Show the next IPFS Tour topic.",
41
+ Run: tourNextCmd,
42
+}
43
+
44
+var cmdIpfsTourList = &commander.Command{
45
+ UsageLine: "list",
46
+ Short: "Show a list of IPFS Tour topics.",
47
+ Run: tourListCmd,
48
+}
49
+
50
+var cmdIpfsTourRestart = &commander.Command{
51
+ UsageLine: "restart",
52
+ Short: "Restart the IPFS Tour.",
53
+ Run: tourRestartCmd,
54
+}
55
+
56
+func tourCmd(c *commander.Command, inp []string) error {
57
+ cfg, err := getConfig(c)
58
+ if err != nil {
59
+ return err
60
+ }
61
+
62
+ topic := tour.TopicID(cfg.Tour.Last)
63
+ if len(inp) > 0 {
64
+ topic = tour.TopicID(inp[0])
65
+ }
66
+ return tourShow(topic)
67
+}
68
+
69
+func tourNextCmd(c *commander.Command, _ []string) error {
70
+ cfg, err := getConfig(c)
71
+ if err != nil {
72
+ return err
73
+ }
74
+
75
+ topic := tour.NextTopic(tour.TopicID(cfg.Tour.Last))
76
+ if err := tourShow(topic); err != nil {
77
+ return err
78
+ }
79
+
80
+ // if topic didn't change (last) done
81
+ if string(topic) == cfg.Tour.Last {
82
+ return nil
83
+ }
84
+
85
+ // topic changed, not last. write it out.
86
+ cfg.Tour.Last = string(topic)
87
+ return writeConfig(c, cfg)
88
+}
89
+
90
+func tourListCmd(c *commander.Command, _ []string) error {
91
+ cfg, err := getConfig(c)
92
+ if err != nil {
93
+ return err
94
+ }
95
+ lastid := tour.TopicID(cfg.Tour.Last)
96
+
97
+ for _, id := range tour.IDs {
98
+ c := ' '
99
+ switch {
100
+ case id == lastid:
101
+ c = '*'
102
+ case id.LessThan(lastid):
103
+ c = '✓'
104
+ }
105
+
106
+ t := tour.Topics[id]
107
+ fmt.Printf("- %c %5.5s %s\n", c, id, t.Title)
108
+ }
109
+ return nil
110
+}
111
+
112
+func tourRestartCmd(c *commander.Command, _ []string) error {
113
+ cfg, err := getConfig(c)
114
+ if err != nil {
115
+ return err
116
+ }
117
+
118
+ cfg.Tour.Last = ""
119
+ return writeConfig(c, cfg)
120
+}
121
+
122
+func tourShow(id tour.ID) error {
123
+ t, found := tour.Topics[id]
124
+ if !found {
125
+ return fmt.Errorf("no topic with id: %s", id)
126
+ }
127
+
128
+ fmt.Printf("Tour %s - %s\n\n%s\n", t.ID, t.Title, t.Text)
129
+ return nil
130
+}
131
+
132
+func lastTour(cfg *config.Config) string {
133
+ return ""
134
+}
config/config.go
+7
@@ -47,6 +47,12 @@ func (bp *BootstrapPeer) String() string {
47
return bp.Address + "/" + bp.PeerID
48
}
49
50
+// Tour stores the ipfs tour read-list and resume point
51
+type Tour struct {
52
+ Last string // last tour topic read
53
+ // Done []string // all topics done so far
54
+}
55
+
56
// Config is used to load IPFS config files.
57
type Config struct {
58
Identity Identity // local node's peer identity
@@ -55,6 +61,7 @@ type Config struct {
61
Mounts Mounts // local node's mount points
62
Version Version // local node's version management
63
Bootstrap []*BootstrapPeer // local nodes's bootstrap peers
64
+ Tour Tour // local node's tour position
65
}
66
67
// DefaultPathRoot is the path to the default config dir location.
tour/all.go
new
+26
@@ -0,0 +1,26 @@
1
+package tour
2
+
3
+import "sort"
4
+
5
+func init() {
6
+ for _, t := range allTopics {
7
+ Topics[t.ID] = t
8
+ IDs = append(IDs, t.ID)
9
+ }
10
+
11
+ sort.Sort(IDSlice(IDs))
12
+}
13
+
14
+// Topics contains a mapping of Tour Topic ID to Topic
15
+var allTopics = []Topic{
16
+ Topic{
17
+ ID: ID("0"),
18
+ Title: "Hello Mars",
19
+ Text: "Hello Mars",
20
+ },
21
+ Topic{
22
+ ID: ID("0.1"),
23
+ Title: "Hello Mars 2",
24
+ Text: "Hello Mars 2",
25
+ },
26
+}
tour/tour.go
new
+86
@@ -0,0 +1,86 @@
1
+package tour
2
+
3
+import (
4
+ "strconv"
5
+ "strings"
6
+
7
+ u "github.com/jbenet/go-ipfs/util"
8
+)
9
+
10
+var log = u.Logger("tour")
11
+
12
+// ID is a string identifier for topics
13
+type ID string
14
+
15
+// LessThan returns whether this ID is sorted earlier than another.
16
+func (i ID) LessThan(o ID) bool {
17
+ return compareDottedInts(string(i), string(o))
18
+}
19
+
20
+// IDSlice implements the sort interface for ID slices.
21
+type IDSlice []ID
22
+
23
+func (a IDSlice) Len() int { return len(a) }
24
+func (a IDSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
25
+func (a IDSlice) Less(i, j int) bool { return a[i].LessThan(a[j]) }
26
+
27
+// Topic is a type of objects that structures a tour topic.
28
+type Topic struct {
29
+ ID ID
30
+ Title string
31
+ Text string
32
+}
33
+
34
+// Topics is a sorted list of topic IDs
35
+var IDs []ID
36
+
37
+// Topics contains a mapping of Tour Topic ID to Topic
38
+var Topics = map[ID]Topic{}
39
+
40
+// NextTopic returns the next in-order topic
41
+func NextTopic(topic ID) ID {
42
+ for _, id := range IDs {
43
+ if topic.LessThan(id) {
44
+ return id
45
+ }
46
+ }
47
+ return topic // last one, it seems.
48
+}
49
+
50
+// TopicID returns a valid tour topic ID from given string
51
+func TopicID(t string) ID {
52
+ if t == "" { // if empty, use first ID
53
+ return IDs[0]
54
+ }
55
+ return ID(t)
56
+}
57
+
58
+func compareDottedInts(i, o string) bool {
59
+ is := strings.Split(i, ".")
60
+ os := strings.Split(o, ".")
61
+
62
+ for n, vis := range is {
63
+ if n >= len(os) {
64
+ return false // os is smaller.
65
+ }
66
+
67
+ vos := os[n]
68
+ ivis, err1 := strconv.Atoi(vis)
69
+ ivos, err2 := strconv.Atoi(vos)
70
+ if err1 != nil || err2 != nil {
71
+ log.Error(err1)
72
+ log.Error(err2)
73
+ panic("tour ID LessThan: not an int")
74
+ }
75
+
76
+ if ivis < ivos {
77
+ return true
78
+ }
79
+
80
+ if ivis > ivos {
81
+ return false
82
+ }
83
+ }
84
+
85
+ return len(os) > len(is)
86
+}