aboutsummaryrefslogtreecommitdiffstats
path: root/commands/patch/find.go
blob: a508551da79250cdf1a1dd804f507e19a8c9cf54 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package patch

import (
	"errors"
	"fmt"
	"net/textproto"
	"strings"

	"git.sr.ht/~rjarry/aerc/app"
	"git.sr.ht/~rjarry/aerc/commands"
	"git.sr.ht/~rjarry/aerc/commands/account"
	"git.sr.ht/~rjarry/aerc/lib/pama"
	"git.sr.ht/~rjarry/aerc/lib/pama/models"
	"git.sr.ht/~rjarry/go-opt"
)

type Find struct {
	Filter bool     `opt:"-f"`
	Commit []string `opt:"..." required:"true" complete:"Complete"`
}

func init() {
	register(Find{})
}

func (Find) Context() commands.CommandContext {
	return commands.GLOBAL
}

func (Find) Aliases() []string {
	return []string{"find"}
}

func (*Find) Complete(arg string) []string {
	m := pama.New()
	p, err := m.CurrentProject()
	if err != nil {
		return nil
	}

	options := make([]string, len(p.Commits))
	for i, c := range p.Commits {
		options[i] = fmt.Sprintf("%-6.6s %s", c.ID, c.Subject)
	}

	return commands.FilterList(options, arg, nil)
}

func (s Find) Execute(_ []string) error {
	m := pama.New()
	p, err := m.CurrentProject()
	if err != nil {
		return err
	}

	if len(s.Commit) == 0 {
		return errors.New("missing commit hash")
	}

	lexed := opt.LexArgs(strings.TrimSpace(s.Commit[0]))

	hash, err := lexed.ArgSafe(0)
	if err != nil {
		return err
	}

	if len(hash) < 4 {
		return errors.New("Commit hash is too short.")
	}

	var c models.Commit
	for _, commit := range p.Commits {
		if strings.Contains(commit.ID, hash) {
			c = commit
			break
		}
	}
	if c.ID == "" {
		var err error
		c, err = m.Find(hash, p)
		if err != nil {
			return err
		}
	}

	// If Message-Id is provided, find it in store
	if c.MessageId != "" {
		if selectMessageId(c.MessageId) {
			return nil
		}
	}

	// Fallback to a search based on the subject line
	args := []string{"search"}
	if s.Filter {
		args[0] = "filter"
	}

	headers := make(textproto.MIMEHeader)
	args = append(args, fmt.Sprintf("-H Subject:%s", c.Subject))
	headers.Add("Subject", c.Subject)

	cmd := account.SearchFilter{
		Headers: headers,
	}

	return cmd.Execute(args)
}

func selectMessageId(msgid string) bool {
	acct := app.SelectedAccount()
	if acct == nil {
		return false
	}
	store := acct.Store()
	if store == nil {
		return false
	}
	for uid, msg := range store.Messages {
		if msg == nil {
			continue
		}
		if msg.RFC822Headers == nil {
			continue
		}
		id, err := msg.RFC822Headers.MessageID()
		if err != nil {
			continue
		}
		if id == msgid {
			store.Select(uid)
			return true
		}
	}
	return false
}