campaigner/cmd/add.go

188 lines
4.5 KiB
Go
Raw Normal View History

2020-02-29 00:59:54 +01:00
package cmd
import (
2020-02-29 17:45:53 +01:00
"bufio"
2020-03-07 11:11:40 +01:00
"encoding/csv"
"fmt"
2020-02-29 17:45:53 +01:00
"os"
"strconv"
"strings"
2020-02-29 00:59:54 +01:00
"github.com/spf13/cobra"
2020-02-29 01:20:46 +01:00
2020-02-29 13:40:39 +01:00
"git.ctrlz.es/mgdelacroix/campaigner/campaign"
2020-02-29 01:20:46 +01:00
"git.ctrlz.es/mgdelacroix/campaigner/model"
2020-02-29 00:59:54 +01:00
)
func GrepAddCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "grep",
Short: "Generates the tickets reading grep's output from stdin",
Long: `Generates tickets for the campaign reading from the standard input the output grep. The grep command must be run with the -n flag. The generated ticket will contain three fields:
- filename: the filename yield by grep
- lineNo: the line number yield by grep
- text: the trimmed line that grep captured for the expression
`,
Example: ` grep -nriIF --include \*.go cobra.Command | campaigner add grep`,
Args: cobra.NoArgs,
Run: grepAddCmdF,
}
cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match")
return cmd
}
func AgAddCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "ag",
2020-03-04 22:22:16 +01:00
Short: "Generates the tickets reading ag's output from stdin",
Long: "Generates tickets for the campaign reading from the standard input the output ag",
Example: ` ag cobra.Command | campaigner add ag`,
Args: cobra.NoArgs,
RunE: agAddCmdF,
}
cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match")
return cmd
}
func GovetAddCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "govet",
2020-03-04 22:22:16 +01:00
Short: "Generates the tickets reading govet's output from stdin",
Long: "Generates tickets for the campaign reading from the standard input the output grep. The grep command must be run with the -json flag",
Example: ` govet -json ./... | campaigner add govet`,
Args: cobra.NoArgs,
RunE: govetAddCmdF,
}
cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match")
return cmd
}
func CsvAddCmd() *cobra.Command {
2020-03-07 11:11:40 +01:00
return &cobra.Command{
Use: "csv",
2020-03-04 22:22:16 +01:00
Short: "Generates the tickets reading a csv file",
2020-03-07 11:11:40 +01:00
Example: ` campaigner add csv tickets.csv`,
Args: cobra.ExactArgs(1),
Run: csvAddCmdF,
}
}
2020-02-29 00:59:54 +01:00
func AddCmd() *cobra.Command {
2020-02-29 01:20:46 +01:00
cmd := &cobra.Command{
2020-02-29 00:59:54 +01:00
Use: "add",
2020-04-27 12:22:15 +02:00
Short: "Adds tickets to the campaign",
Long: "Adds tickets to the campaign from the output of grep/ag/govet",
2020-02-29 00:59:54 +01:00
}
cmd.AddCommand(
GrepAddCmd(),
AgAddCmd(),
GovetAddCmd(),
CsvAddCmd(),
)
2020-02-29 01:20:46 +01:00
return cmd
}
2020-02-29 17:45:53 +01:00
func parseGrepLine(line string) (*model.Ticket, error) {
2020-02-29 13:11:36 +01:00
// ToDo: it would be great to be able to relate a line with its
// parent method, at least for JS and Golang
parts := strings.Split(line, ":")
if len(parts) < 3 {
return nil, fmt.Errorf("cannot parse line: %s", line)
}
filename := parts[0]
lineNo, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}
text := strings.Join(parts[2:], "")
2020-02-29 17:45:53 +01:00
return &model.Ticket{
2020-03-04 23:07:26 +01:00
Data: map[string]interface{}{
"filename": filename,
"lineNo": lineNo,
"text": strings.TrimSpace(text),
2020-03-04 23:07:26 +01:00
},
2020-02-29 17:45:53 +01:00
}, nil
}
2020-02-29 17:45:53 +01:00
func parseGrep() []*model.Ticket {
tickets := []*model.Ticket{}
2020-02-29 17:45:53 +01:00
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
ticket, _ := parseGrepLine(scanner.Text())
if ticket != nil {
tickets = append(tickets, ticket)
}
}
2020-02-29 17:45:53 +01:00
return tickets
}
func grepAddCmdF(cmd *cobra.Command, _ []string) {
2020-03-01 13:21:10 +01:00
fileOnly, _ := cmd.Flags().GetBool("file-only")
2020-02-29 14:29:32 +01:00
tickets := parseGrep()
2020-02-29 13:40:39 +01:00
cmp, err := campaign.Read()
if err != nil {
ErrorAndExit(cmd, err)
}
cmp.Tickets = append(cmp.Tickets, tickets...)
2020-03-01 13:21:10 +01:00
cmp.Tickets = model.RemoveDuplicateTickets(cmp.Tickets, fileOnly)
2020-02-29 13:40:39 +01:00
if err := campaign.Save(cmp); err != nil {
ErrorAndExit(cmd, err)
}
2020-03-07 12:04:24 +01:00
cmd.Printf("%d tickets have been added\n", len(tickets))
}
func agAddCmdF(_ *cobra.Command, _ []string) error {
return fmt.Errorf("not implemented yet")
}
func govetAddCmdF(_ *cobra.Command, _ []string) error {
return fmt.Errorf("not implemented yet")
}
2020-03-07 11:11:40 +01:00
func csvAddCmdF(cmd *cobra.Command, args []string) {
file, err := os.Open(args[0])
if err != nil {
ErrorAndExit(cmd, err)
}
cmp, err := campaign.Read()
if err != nil {
ErrorAndExit(cmd, err)
}
csvReader := csv.NewReader(bufio.NewReader(file))
records, err := csvReader.ReadAll()
if err != nil {
ErrorAndExit(cmd, err)
}
headers := records[0]
for _, line := range records[1:] {
data := map[string]interface{}{}
for i, header := range headers {
data[header] = line[i]
}
cmp.Tickets = append(cmp.Tickets, &model.Ticket{Data: data})
}
if err := campaign.Save(cmp); err != nil {
ErrorAndExit(cmd, err)
}
2020-03-07 12:04:24 +01:00
cmd.Printf("%d tickets have been added\n", len(records[1:]))
2020-02-29 00:59:54 +01:00
}