init: pristine aerc 0.20.0 source
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/format"
|
||||
"git.sr.ht/~rjarry/aerc/lib/parse"
|
||||
"git.sr.ht/~rjarry/aerc/models"
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
var version string
|
||||
|
||||
// SetVersion initializes the aerc version displayed in template functions
|
||||
func SetVersion(v string) {
|
||||
version = v
|
||||
}
|
||||
|
||||
var execPath string
|
||||
|
||||
func SetExecPath(dirs []string) {
|
||||
// prepend aerc filters dirs to the default exec path
|
||||
paths := make([]string, 0, len(dirs)+1)
|
||||
for _, d := range dirs {
|
||||
paths = append(paths, filepath.Join(d, "filters"))
|
||||
}
|
||||
paths = append(paths, os.Getenv("PATH"))
|
||||
execPath = strings.Join(paths, ":")
|
||||
}
|
||||
|
||||
// wrap allows to chain wrapText
|
||||
func wrap(lineWidth int, text string) string {
|
||||
return wrapText(text, lineWidth)
|
||||
}
|
||||
|
||||
func wrapLine(text string, lineWidth int) string {
|
||||
words := strings.Fields(text)
|
||||
if len(words) == 0 {
|
||||
return text
|
||||
}
|
||||
var wrapped strings.Builder
|
||||
wrapped.WriteString(words[0])
|
||||
spaceLeft := lineWidth - wrapped.Len()
|
||||
for _, word := range words[1:] {
|
||||
if len(word)+1 > spaceLeft {
|
||||
wrapped.WriteRune('\n')
|
||||
wrapped.WriteString(word)
|
||||
spaceLeft = lineWidth - len(word)
|
||||
} else {
|
||||
wrapped.WriteRune(' ')
|
||||
wrapped.WriteString(word)
|
||||
spaceLeft -= 1 + len(word)
|
||||
}
|
||||
}
|
||||
|
||||
return wrapped.String()
|
||||
}
|
||||
|
||||
func wrapText(text string, lineWidth int) string {
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
text = strings.TrimRight(text, "\n")
|
||||
lines := strings.Split(text, "\n")
|
||||
var wrapped strings.Builder
|
||||
|
||||
for _, line := range lines {
|
||||
switch {
|
||||
case line == "":
|
||||
// deliberately left blank
|
||||
case line[0] == '>':
|
||||
// leave quoted text alone
|
||||
wrapped.WriteString(line)
|
||||
default:
|
||||
wrapped.WriteString(wrapLine(line, lineWidth))
|
||||
}
|
||||
wrapped.WriteRune('\n')
|
||||
}
|
||||
return wrapped.String()
|
||||
}
|
||||
|
||||
// quote prepends "> " in front of every line in text
|
||||
func quote(text string) string {
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
text = strings.TrimRight(text, "\n")
|
||||
lines := strings.Split(text, "\n")
|
||||
var quoted strings.Builder
|
||||
for _, line := range lines {
|
||||
if line == "" {
|
||||
quoted.WriteString(">\n")
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, ">") {
|
||||
quoted.WriteString(">")
|
||||
} else {
|
||||
quoted.WriteString("> ")
|
||||
}
|
||||
quoted.WriteString(line)
|
||||
quoted.WriteRune('\n')
|
||||
}
|
||||
|
||||
return quoted.String()
|
||||
}
|
||||
|
||||
// cmd allow to parse reply by shell command
|
||||
// text have to be passed by cmd param
|
||||
// if there is error, original string is returned
|
||||
func cmd(cmd, text string) string {
|
||||
var out bytes.Buffer
|
||||
c := exec.Command("sh", "-c", cmd)
|
||||
c.Env = append(os.Environ(), "PATH="+execPath)
|
||||
c.Stdin = strings.NewReader(text)
|
||||
c.Stdout = &out
|
||||
err := c.Run()
|
||||
if err != nil {
|
||||
return text
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func toLocal(t time.Time) time.Time {
|
||||
return time.Time.In(t, time.Local)
|
||||
}
|
||||
|
||||
func rearrangeNameWithComma(name string) string {
|
||||
parts := strings.SplitN(name, ",", 3)
|
||||
if len(parts) == 2 {
|
||||
return fmt.Sprintf("%s %s", strings.TrimSpace(parts[1]), strings.TrimSpace(parts[0]))
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func names(addresses []*mail.Address) []string {
|
||||
n := make([]string, len(addresses))
|
||||
for i, addr := range addresses {
|
||||
name := rearrangeNameWithComma(addr.Name)
|
||||
if name == "" {
|
||||
parts := strings.SplitN(addr.Address, "@", 2)
|
||||
name = parts[0]
|
||||
}
|
||||
n[i] = name
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func firstnames(addresses []*mail.Address) []string {
|
||||
n := make([]string, len(addresses))
|
||||
for i, addr := range addresses {
|
||||
var name string
|
||||
if addr.Name == "" {
|
||||
parts := strings.SplitN(addr.Address, "@", 2)
|
||||
parts = strings.SplitN(parts[0], ".", 2)
|
||||
name = parts[0]
|
||||
} else {
|
||||
name = rearrangeNameWithComma(addr.Name)
|
||||
name = strings.SplitN(name, " ", 2)[0] // split by spaces and get the first word
|
||||
name = strings.SplitN(name, ",", 2)[0] // split by commas and get the first word
|
||||
}
|
||||
n[i] = name
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func initials(addresses []*mail.Address) []string {
|
||||
n := names(addresses)
|
||||
ret := make([]string, len(addresses))
|
||||
for i, name := range n {
|
||||
split := strings.Split(name, " ")
|
||||
initial := ""
|
||||
for _, s := range split {
|
||||
initial += string([]rune(s)[0:1])
|
||||
}
|
||||
ret[i] = initial
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func emails(addresses []*mail.Address) []string {
|
||||
e := make([]string, len(addresses))
|
||||
for i, addr := range addresses {
|
||||
e[i] = addr.Address
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func mboxes(addresses []*mail.Address) []string {
|
||||
e := make([]string, len(addresses))
|
||||
for i, addr := range addresses {
|
||||
parts := strings.SplitN(addr.Address, "@", 2)
|
||||
e[i] = parts[0]
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func shortmboxes(addresses []*mail.Address) []string {
|
||||
e := make([]string, len(addresses))
|
||||
for i, addr := range addresses {
|
||||
parts := strings.SplitN(addr.Address, "@", 2)
|
||||
parts = strings.SplitN(parts[0], ".", 2)
|
||||
e[i] = parts[0]
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func persons(addresses []*mail.Address) []string {
|
||||
e := make([]string, len(addresses))
|
||||
for i, addr := range addresses {
|
||||
e[i] = format.AddressForHumans(addr)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
var units = []string{"K", "M", "G", "T"}
|
||||
|
||||
func humanReadable(value int) string {
|
||||
sign := ""
|
||||
if value < 0 {
|
||||
sign = "-"
|
||||
value = -value
|
||||
}
|
||||
if value < 1000 {
|
||||
return fmt.Sprintf("%s%d", sign, value)
|
||||
}
|
||||
val := float64(value)
|
||||
unit := ""
|
||||
for i := 0; val >= 1000 && i < len(units); i++ {
|
||||
unit = units[i]
|
||||
val /= 1000.0
|
||||
}
|
||||
if val < 100.0 {
|
||||
return fmt.Sprintf("%s%.1f%s", sign, val, unit)
|
||||
}
|
||||
return fmt.Sprintf("%s%.0f%s", sign, val, unit)
|
||||
}
|
||||
|
||||
func cwd() string {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if strings.HasPrefix(path, home) {
|
||||
path = strings.Replace(path, home, "~", 1)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func join(sep string, elems []string) string {
|
||||
return strings.Join(elems, sep)
|
||||
}
|
||||
|
||||
func split(sep string, s string) []string {
|
||||
return strings.Split(s, sep)
|
||||
}
|
||||
|
||||
// removes a signature from the piped in message
|
||||
func trimSignature(message string) string {
|
||||
var res strings.Builder
|
||||
|
||||
input := bufio.NewScanner(strings.NewReader(message))
|
||||
|
||||
for input.Scan() {
|
||||
line := input.Text()
|
||||
if line == "-- " {
|
||||
break
|
||||
}
|
||||
res.WriteString(line)
|
||||
res.WriteRune('\n')
|
||||
}
|
||||
return res.String()
|
||||
}
|
||||
|
||||
func compactDir(path string) string {
|
||||
return format.CompactPath(path, os.PathSeparator)
|
||||
}
|
||||
|
||||
type (
|
||||
Case struct{ expr, value string }
|
||||
Default struct{ value string }
|
||||
Exclude struct{ expr string }
|
||||
)
|
||||
|
||||
func (c *Case) Matches(s string) bool { return parse.MatchCache(s, c.expr) }
|
||||
func (c *Case) Value() string { return c.value }
|
||||
func (c *Case) Skip() bool { return false }
|
||||
func (d *Default) Matches(s string) bool { return true }
|
||||
func (d *Default) Value() string { return d.value }
|
||||
func (d *Default) Skip() bool { return false }
|
||||
func (e *Exclude) Matches(s string) bool { return parse.MatchCache(s, e.expr) }
|
||||
func (e *Exclude) Value() string { return "" }
|
||||
func (e *Exclude) Skip() bool { return true }
|
||||
|
||||
func switch_(value string, cases ...models.Case) string {
|
||||
for _, c := range cases {
|
||||
if c.Matches(value) {
|
||||
return c.Value()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func case_(expr, value string) models.Case {
|
||||
return &Case{expr: expr, value: value}
|
||||
}
|
||||
|
||||
func default_(value string) models.Case {
|
||||
return &Default{value: value}
|
||||
}
|
||||
|
||||
func exclude(expr string) models.Case {
|
||||
return &Exclude{expr: expr}
|
||||
}
|
||||
|
||||
func map_(elements []string, cases ...models.Case) []string {
|
||||
mapped := make([]string, 0, len(elements))
|
||||
top:
|
||||
for _, e := range elements {
|
||||
for _, c := range cases {
|
||||
if c.Matches(e) {
|
||||
if c.Skip() {
|
||||
continue top
|
||||
}
|
||||
e = c.Value()
|
||||
break
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, e)
|
||||
}
|
||||
return mapped
|
||||
}
|
||||
|
||||
func replace(pattern, subst, value string) string {
|
||||
re := regexp.MustCompile(pattern)
|
||||
return re.ReplaceAllString(value, subst)
|
||||
}
|
||||
|
||||
func contains(substring, s string) bool {
|
||||
return strings.Contains(s, substring)
|
||||
}
|
||||
|
||||
func hasPrefix(prefix, s string) bool {
|
||||
return strings.HasPrefix(s, prefix)
|
||||
}
|
||||
|
||||
func head(n uint, s string) string {
|
||||
r := []rune(s)
|
||||
length := uint(len(r))
|
||||
if length >= n {
|
||||
return string(r[:n])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func tail(n uint, s string) string {
|
||||
r := []rune(s)
|
||||
length := uint(len(r))
|
||||
if length >= n {
|
||||
return string(r[length-n:])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var templateFuncs = template.FuncMap{
|
||||
"quote": quote,
|
||||
"wrapText": wrapText,
|
||||
"wrap": wrap,
|
||||
"now": time.Now,
|
||||
"dateFormat": time.Time.Format,
|
||||
"toLocal": toLocal,
|
||||
"exec": cmd,
|
||||
"version": func() string { return version },
|
||||
"names": names,
|
||||
"firstnames": firstnames,
|
||||
"initials": initials,
|
||||
"emails": emails,
|
||||
"mboxes": mboxes,
|
||||
"shortmboxes": shortmboxes,
|
||||
"persons": persons,
|
||||
"humanReadable": humanReadable,
|
||||
"cwd": cwd,
|
||||
"join": join,
|
||||
"split": split,
|
||||
"trimSignature": trimSignature,
|
||||
"compactDir": compactDir,
|
||||
"match": parse.MatchCache,
|
||||
"switch": switch_,
|
||||
"case": case_,
|
||||
"default": default_,
|
||||
"map": map_,
|
||||
"exclude": exclude,
|
||||
"contains": contains,
|
||||
"hasPrefix": hasPrefix,
|
||||
"toLower": strings.ToLower,
|
||||
"toUpper": strings.ToUpper,
|
||||
"replace": replace,
|
||||
"head": head,
|
||||
"tail": tail,
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
func TestTemplates_DifferentNamesFormats(t *testing.T) {
|
||||
type testCase struct {
|
||||
address mail.Address
|
||||
name string
|
||||
}
|
||||
|
||||
cases := []testCase{
|
||||
{address: mail.Address{Name: "", Address: "john@doe.com"}, name: "john"},
|
||||
{address: mail.Address{Name: "", Address: "bill.john.doe@doe.com"}, name: "bill.john.doe"},
|
||||
{address: mail.Address{Name: "John", Address: "john@doe.com"}, name: "John"},
|
||||
{address: mail.Address{Name: "John Doe", Address: "john@doe.com"}, name: "John Doe"},
|
||||
{address: mail.Address{Name: "Bill John Doe", Address: "john@doe.com"}, name: "Bill John Doe"},
|
||||
{address: mail.Address{Name: "Doe, John", Address: "john@doe.com"}, name: "John Doe"},
|
||||
{address: mail.Address{Name: "Doe, Bill John", Address: "john@doe.com"}, name: "Bill John Doe"},
|
||||
{address: mail.Address{Name: "Schröder, Gerhard", Address: "s@g.de"}, name: "Gerhard Schröder"},
|
||||
{address: mail.Address{Name: "Buhl-Freiherr von und zu Guttenberg, Karl-Theodor Maria Nikolaus Johann Jacob Philipp Franz Joseph Sylvester", Address: "long@email.com"}, name: "Karl-Theodor Maria Nikolaus Johann Jacob Philipp Franz Joseph Sylvester Buhl-Freiherr von und zu Guttenberg"},
|
||||
{address: mail.Address{Name: "Dr. Őz-Szűcs Villő, MD, PhD, MBA (Üllői úti Klinika, Budapest, Hungary)", Address: "a@b.com"}, name: "Dr. Őz-Szűcs Villő, MD, PhD, MBA (Üllői úti Klinika, Budapest, Hungary)"},
|
||||
{address: mail.Address{Name: "International Important Conference, 2023", Address: "a@b.com"}, name: "2023 International Important Conference"},
|
||||
{address: mail.Address{Name: "A. B.C. Muscat", Address: "a@b.com"}, name: "A. B.C. Muscat"},
|
||||
{address: mail.Address{Name: "Wertram, te, K.W.", Address: "a@b.com"}, name: "Wertram, te, K.W."},
|
||||
{address: mail.Address{Name: "Harvard, John, Dr. CDC/MIT/SYSOPSYS", Address: "a@b.com"}, name: "Harvard, John, Dr. CDC/MIT/SYSOPSYS"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
names := names([]*mail.Address{&c.address})
|
||||
assert.Len(t, names, 1)
|
||||
assert.Equal(t, c.name, names[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplates_DifferentFirstnamesFormats(t *testing.T) {
|
||||
type testCase struct {
|
||||
address mail.Address
|
||||
firstname string
|
||||
}
|
||||
|
||||
cases := []testCase{
|
||||
{address: mail.Address{Name: "", Address: "john@doe.com"}, firstname: "john"},
|
||||
{address: mail.Address{Name: "", Address: "bill.john.doe@doe.com"}, firstname: "bill"},
|
||||
{address: mail.Address{Name: "John", Address: "john@doe.com"}, firstname: "John"},
|
||||
{address: mail.Address{Name: "John Doe", Address: "john@doe.com"}, firstname: "John"},
|
||||
{address: mail.Address{Name: "Bill John Doe", Address: "john@doe.com"}, firstname: "Bill"},
|
||||
{address: mail.Address{Name: "Doe, John", Address: "john@doe.com"}, firstname: "John"},
|
||||
{address: mail.Address{Name: "Schröder, Gerhard", Address: "s@g.de"}, firstname: "Gerhard"},
|
||||
{address: mail.Address{Name: "Buhl-Freiherr von und zu Guttenberg, Karl-Theodor Maria Nikolaus Johann Jacob Philipp Franz Joseph Sylvester", Address: "long@email.com"}, firstname: "Karl-Theodor"},
|
||||
{address: mail.Address{Name: "Dr. Őz-Szűcs Villő, MD, PhD, MBA (Üllői úti Klinika, Budapest, Hungary)", Address: "a@b.com"}, firstname: "Dr."},
|
||||
{address: mail.Address{Name: "International Important Conference, 2023", Address: "a@b.com"}, firstname: "2023"},
|
||||
{address: mail.Address{Name: "A. B.C. Muscat", Address: "a@b.com"}, firstname: "A."},
|
||||
{address: mail.Address{Name: "Wertram, te, K.W.", Address: "a@b.com"}, firstname: "Wertram"},
|
||||
{address: mail.Address{Name: "Harvard, John, Dr. CDC/MIT/SYSOPSYS", Address: "a@b.com"}, firstname: "Harvard"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
names := firstnames([]*mail.Address{&c.address})
|
||||
assert.Len(t, names, 1)
|
||||
assert.Equal(t, c.firstname, names[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplates_InternalRearrangeNamesWithComma(t *testing.T) {
|
||||
type testCase struct {
|
||||
source string
|
||||
res string
|
||||
}
|
||||
|
||||
cases := []testCase{
|
||||
{source: "John.Doe", res: "John.Doe"},
|
||||
{source: "John Doe", res: "John Doe"},
|
||||
{source: "John Bill Doe", res: "John Bill Doe"},
|
||||
{source: "Doe, John Bill", res: "John Bill Doe"},
|
||||
{source: "Doe, John-Bill", res: "John-Bill Doe"},
|
||||
{source: "Doe John, Bill", res: "Bill Doe John"},
|
||||
{source: "Schröder, Gerhard", res: "Gerhard Schröder"},
|
||||
// do not touch names with more than one comma
|
||||
{source: "One, Two, Three", res: "One, Two, Three"},
|
||||
{source: "One, Two, Three, Four", res: "One, Two, Three, Four"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
res := rearrangeNameWithComma(c.source)
|
||||
assert.Equal(t, c.res, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplates_DifferentInitialsFormats(t *testing.T) {
|
||||
type testCase struct {
|
||||
address mail.Address
|
||||
initials string
|
||||
}
|
||||
|
||||
cases := []testCase{
|
||||
{address: mail.Address{Name: "", Address: "john@doe.com"}, initials: "j"},
|
||||
{address: mail.Address{Name: "", Address: "bill.john.doe@doe.com"}, initials: "b"},
|
||||
{address: mail.Address{Name: "John", Address: "john@doe.com"}, initials: "J"},
|
||||
{address: mail.Address{Name: "John Doe", Address: "john@doe.com"}, initials: "JD"},
|
||||
{address: mail.Address{Name: "Bill John Doe", Address: "john@doe.com"}, initials: "BJD"},
|
||||
{address: mail.Address{Name: "Doe, John", Address: "john@doe.com"}, initials: "JD"},
|
||||
{address: mail.Address{Name: "Doe, John Bill", Address: "john@doe.com"}, initials: "JBD"},
|
||||
{address: mail.Address{Name: "Schröder, Gerhard", Address: "s@g.de"}, initials: "GS"},
|
||||
{address: mail.Address{Name: "Buhl-Freiherr von und zu Guttenberg, Karl-Theodor Maria Nikolaus Johann Jacob Philipp Franz Joseph Sylvester", Address: "long@email.com"}, initials: "KMNJJPFJSBvuzG"},
|
||||
{address: mail.Address{Name: "Dr. Őz-Szűcs Villő, MD, PhD, MBA (Üllői úti Klinika, Budapest, Hungary)", Address: "a@b.com"}, initials: "DŐVMPM(úKBH"},
|
||||
{address: mail.Address{Name: "International Important Conference, 2023", Address: "a@b.com"}, initials: "2IIC"},
|
||||
{address: mail.Address{Name: "A. B.C. Muscat", Address: "a@b.com"}, initials: "ABM"},
|
||||
{address: mail.Address{Name: "Wertram, te, K.W.", Address: "a@b.com"}, initials: "WtK"},
|
||||
{address: mail.Address{Name: "Harvard, John, Dr. CDC/MIT/SYSOPSYS", Address: "a@b.com"}, initials: "HJDC"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
intls := initials([]*mail.Address{&c.address})
|
||||
assert.Len(t, intls, 1)
|
||||
assert.Equal(t, c.initials, intls[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplates_Head(t *testing.T) {
|
||||
type testCase struct {
|
||||
head uint
|
||||
input string
|
||||
output string
|
||||
}
|
||||
cases := []testCase{
|
||||
{head: 3, input: "abcde", output: "abc"},
|
||||
{head: 10, input: "abcde", output: "abcde"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
out := head(c.head, c.input)
|
||||
assert.Equal(t, c.output, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplates_Tail(t *testing.T) {
|
||||
type testCase struct {
|
||||
tail uint
|
||||
input string
|
||||
output string
|
||||
}
|
||||
cases := []testCase{
|
||||
{tail: 2, input: "abcde", output: "de"},
|
||||
{tail: 8, input: "abcde", output: "abcde"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
out := tail(c.tail, c.input)
|
||||
assert.Equal(t, c.output, out)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"text/template"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/xdg"
|
||||
"git.sr.ht/~rjarry/aerc/models"
|
||||
)
|
||||
|
||||
func findTemplate(templateName string, templateDirs []string) (string, error) {
|
||||
for _, dir := range templateDirs {
|
||||
templateFile := xdg.ExpandHome(dir, templateName)
|
||||
if _, err := os.Stat(templateFile); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return templateFile, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf(
|
||||
"Can't find template %q in any of %v ", templateName, templateDirs)
|
||||
}
|
||||
|
||||
func ParseTemplateFromFile(
|
||||
name string, dirs []string, data models.TemplateData,
|
||||
) (io.Reader, error) {
|
||||
templateFile, err := findTemplate(name, dirs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
emailTemplate, err := template.New(name).
|
||||
Funcs(templateFuncs).ParseFiles(templateFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var body bytes.Buffer
|
||||
if err := Render(emailTemplate, &body, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &body, nil
|
||||
}
|
||||
|
||||
func ParseTemplate(name, content string) (*template.Template, error) {
|
||||
return template.New(name).Funcs(templateFuncs).Parse(content)
|
||||
}
|
||||
|
||||
func Render(t *template.Template, w io.Writer, data models.TemplateData) error {
|
||||
return t.Execute(w, data)
|
||||
}
|
||||
|
||||
// builtins is a slice of keywords and functions built into the Go standard
|
||||
// library for templates. Since they are not exported, they are hardcoded here.
|
||||
var builtins = []string{
|
||||
// from the Go standard library: src/text/template/parse/lex.go
|
||||
"block",
|
||||
"break",
|
||||
"continue",
|
||||
"define",
|
||||
"else",
|
||||
"end",
|
||||
"if",
|
||||
"range",
|
||||
"nil",
|
||||
"template",
|
||||
"with",
|
||||
|
||||
// from the Go standard library: src/text/template/funcs.go
|
||||
"and",
|
||||
"call",
|
||||
"html",
|
||||
"index",
|
||||
"slice",
|
||||
"js",
|
||||
"len",
|
||||
"not",
|
||||
"or",
|
||||
"print",
|
||||
"printf",
|
||||
"println",
|
||||
"urlquery",
|
||||
"eq",
|
||||
"ge",
|
||||
"gt",
|
||||
"le",
|
||||
"lt",
|
||||
"ne",
|
||||
}
|
||||
|
||||
func Terms() []string {
|
||||
var s []string
|
||||
t := reflect.TypeOf((*models.TemplateData)(nil)).Elem()
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
s = append(s, "."+t.Method(i).Name)
|
||||
}
|
||||
for fnStr := range templateFuncs {
|
||||
s = append(s, fnStr)
|
||||
}
|
||||
s = append(s, builtins...)
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user