github.com/windmilleng/wat@v0.0.2-0.20180626175338-9349b638e250/cli/wat/populate.go (about) 1 package wat 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 8 "time" 9 10 "os" 11 12 "github.com/spf13/cobra" 13 "github.com/windmilleng/wat/errors" 14 ) 15 16 // This file contains code for both `Populate` and `List` 17 18 const listTTL = time.Hour * 48 19 20 var populateCmd = &cobra.Command{ 21 Use: "populate", 22 Short: "Smartly populate a list of available test commands (and associate files)", 23 Run: populate, 24 } 25 26 func populate(cmd *cobra.Command, args []string) { 27 ctx := context.Background() 28 29 ws, err := GetOrInitWatWorkspace() 30 if err != nil { 31 ws.Fatal("GetWatWorkspace", err) 32 } 33 34 cmds, err := List(ctx, ws, 0 /* always fresh */) 35 if err != nil { 36 ws.Fatal("Populate", err) 37 } 38 39 fmt.Printf("Successfully populated %d commands.\n", len(cmds.Commands)) 40 } 41 42 var listCmd = &cobra.Command{ 43 Use: "list", 44 Short: "List all the commands to choose from", 45 Run: list, 46 } 47 48 func list(cmd *cobra.Command, args []string) { 49 ctx := context.Background() 50 51 ws, err := GetOrInitWatWorkspace() 52 if err != nil { 53 ws.Fatal("GetWatWorkspace", err) 54 } 55 56 cmdList, err := List(ctx, ws, listTTL) 57 if err != nil { 58 ws.Fatal("List", err) 59 } 60 61 encoder := json.NewEncoder(os.Stdout) 62 encoder.SetIndent("", " ") 63 err = encoder.Encode(cmdList) 64 if err != nil { 65 ws.Fatal("Encode", err) 66 } 67 } 68 69 type CommandList struct { 70 Timestamp time.Time 71 Commands []WatCommand 72 } 73 74 // Get the list of commands. 75 // 76 // If there are already commands on disk fresher than the ttl, return those 77 // commands. Otherwise, generates a list of available WatCommands (i.e. command 78 // + associated files) and writes them to disk (.wat/list). 79 // 80 // Set the ttl to 0 to always regenerate. 81 func List(ctx context.Context, ws WatWorkspace, ttl time.Duration) (CommandList, error) { 82 if ttl > 0 { 83 exists, err := ws.Exists(fnameList) 84 if err != nil { 85 return CommandList{}, err 86 } 87 88 if exists { 89 cmdList, err := listFromFile(ws) 90 if err != nil { 91 return cmdList, err 92 } 93 94 if time.Since(cmdList.Timestamp) < ttl { 95 // List is current, yay! 96 return cmdList, nil 97 } 98 } 99 } 100 101 // List is stale or does not exist, (re)populate 102 cmds, err := populateAt(ctx, ws) 103 if err != nil { 104 return CommandList{}, fmt.Errorf("populateAt: %v", err) 105 } 106 107 cmdList := CommandList{ 108 Timestamp: time.Now(), 109 Commands: cmds, 110 } 111 err = cmdList.toFile(ws) 112 if err != nil { 113 return CommandList{}, fmt.Errorf("toFile: %v", err) 114 } 115 116 return cmdList, nil 117 } 118 119 func listFromFile(ws WatWorkspace) (cmdList CommandList, err error) { 120 listContents, err := ws.Read(fnameList) 121 if err != nil { 122 return cmdList, fmt.Errorf("read: %v", err) 123 } 124 125 err = json.Unmarshal(listContents, &cmdList) 126 if err != nil { 127 return cmdList, fmt.Errorf("json.Unmarshal: %v", err) 128 } 129 return cmdList, nil 130 } 131 132 func (cl CommandList) toFile(ws WatWorkspace) error { 133 j := MustJson(cl) 134 return ws.Write(fnameList, j) 135 } 136 137 type WatCommand struct { 138 Command string 139 FilePattern string 140 } 141 142 func (c WatCommand) Empty() bool { 143 return c.Command == "" 144 } 145 146 // PrettyCmd returns a string suitable for prettily printing this cmd to terminal 147 func (c WatCommand) PrettyCmd() string { 148 escapedCmd := fmt.Sprintf("%q", c.Command) 149 escapedCmd = escapedCmd[1 : len(escapedCmd)-1] // remove quotes 150 return TermBold(fmt.Sprintf("$ %s\n", escapedCmd)) 151 } 152 153 func populateAt(ctx context.Context, ws WatWorkspace) ([]WatCommand, error) { 154 result := RunBuiltinPlugins(ctx, ws) 155 156 userResult, err := RunUserPlugins(ctx, ws) 157 if err != nil { 158 return result, errors.Propagatef(err, "RunUserPlugins") 159 } 160 161 result = append(result, userResult...) 162 163 // TODO: dedupe? 164 return result, nil 165 }