-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeany.go
More file actions
153 lines (127 loc) · 4.42 KB
/
geany.go
File metadata and controls
153 lines (127 loc) · 4.42 KB
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// SPDX-FileCopyrightText: 2026 The geany contributors.
// SPDX-License-Identifier: MPL-2.0
// Package geany contains the logo printing functionality.
package geany
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"runtime/debug"
"text/template"
)
// ErrWriterNil indicates that the provided io.Writer is nil and cannot be used.
var ErrWriterNil = errors.New("writer is nil")
// geanyData contains the data that geany provides on itself.
type geanyData struct {
VcsRevision string
VcsTime string
VcsModified string
GoVersion string
}
// logoData is the structure given as data to the templating engine.
// It contains the geany provided build information and the user provided data.
// The user is not required to pass data, in that case nil should be passed. In any case,
// the data accessed in the logo template and provided data must match.
type logoData struct {
Geany geanyData // build information
Values any // user provided data
}
// getBuildInfo is a global variable to mock it easily in tests. Take into account that
// this variable can produce race conditions in parallel testing.
var getBuildInfo = debug.ReadBuildInfo //nolint:gochecknoglobals
// prepareLogoData collects the build information and the user-provided data
// into a logoData structure.
func prepareLogoData(values any) logoData {
result := logoData{
Geany: geanyData{
VcsRevision: "unknown",
VcsTime: "unknown",
VcsModified: "",
GoVersion: "unknown",
},
Values: values,
}
if buildInfo, ok := getBuildInfo(); ok && buildInfo != nil {
result.Geany.GoVersion = buildInfo.GoVersion
for _, s := range buildInfo.Settings {
switch s.Key {
case "vcs.revision":
result.Geany.VcsRevision = s.Value
case "vcs.modified":
if s.Value == "true" {
result.Geany.VcsModified = "*"
}
case "vcs.time":
result.Geany.VcsTime = s.Value
}
}
}
return result
}
// PrintSimpleWriter outputs just the name of the program, the build information
// and, in case, the user given data to a user provided io.Writer.
func PrintSimpleWriter(writer io.Writer, values any) error {
if writer == nil {
return ErrWriterNil
}
revData := prepareLogoData(values)
// normally we have the program's name given as the first argument
if len(os.Args) > 0 && os.Args[0] != "" {
if _, err := fmt.Fprintf(writer, "%s\n", os.Args[0]); err != nil {
return fmt.Errorf("could not write program name: %w", err)
}
}
encoder := json.NewEncoder(writer)
encoder.SetIndent("", " ")
// we suppress the linter here, as we cannot guarantee for users data.
if err := encoder.Encode(revData); err != nil { //nolint:musttag
return fmt.Errorf("could not encode user data: %w", err)
}
if _, err := fmt.Fprintln(writer); err != nil {
return fmt.Errorf("could not write final newline: %w", err)
}
return nil
}
// PrintSimple is a convenience wrapper around PrintSimpleWriter,
// as logos are normally printed to standard output.
func PrintSimple(values any) error {
return PrintSimpleWriter(os.Stdout, values)
}
// PrintLogoWriter takes a text/template string as its parameter and renders it to be the logo.
// It offers the following data for the template:
// - VcsRevision
// - VcsTime
// - VcsModified
// - GoVersion
//
// these can be referenced in the template, e.g., using `{{ .Geany.VcsRevision }}`.
// An additional custom value can be accessed via the Values field. Its type must match the way
// that it is accessed in the logo and is accessed using e.g. `{{ .Values.Foo }}`.
//
// The template is parsed and executed. In case of an error, the program's name and user given
// data are printed as JSON as fallback. The original error and, in case, the error of the fallback
// are returned.
func PrintLogoWriter(writer io.Writer, tmpl string, values any) error {
if writer == nil {
return ErrWriterNil
}
revData := prepareLogoData(values)
logo := template.New("logo")
if _, err := logo.Parse(tmpl); err != nil {
return fmt.Errorf("could not parse template: %w", err)
}
if err := logo.Execute(writer, revData); err != nil {
return errors.Join(err, PrintSimpleWriter(writer, values))
}
if _, err := fmt.Fprintln(writer); err != nil {
return fmt.Errorf("could not write final newline: %w", err)
}
return nil
}
// PrintLogo is a convenience wrapper around PrintLogoWriter,
// as logos are normally printed to standard output.
func PrintLogo(tmpl string, values any) error {
return PrintLogoWriter(os.Stdout, tmpl, values)
}