From 68b88bc76686cfafa70e78990559999bbc034bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20BENO=C3=8ET?= Date: Fri, 19 Jul 2024 21:49:22 +0200 Subject: [PATCH] refactor: fix many linter warnings --- cmd/sslcert/main.go | 84 ++++++++++++--------- cmd/zoneserial/main.go | 25 +++--- main.go | 53 +++++++------ pkg/perfdata/perfdata.go | 138 +++++++++++++++++----------------- pkg/perfdata/perfdata_test.go | 12 +-- pkg/plugin/plugin.go | 40 +++++----- pkg/program/program.go | 2 +- 7 files changed, 191 insertions(+), 163 deletions(-) diff --git a/cmd/sslcert/main.go b/cmd/sslcert/main.go index a618464..d942cc3 100644 --- a/cmd/sslcert/main.go +++ b/cmd/sslcert/main.go @@ -4,19 +4,19 @@ import ( "bufio" "crypto/tls" "crypto/x509" - "errors" "fmt" "net" "net/textproto" "os" + "strconv" "strings" "time" + "github.com/karrick/golf" + "nocternity.net/gomonop/pkg/perfdata" "nocternity.net/gomonop/pkg/plugin" "nocternity.net/gomonop/pkg/program" - - "github.com/karrick/golf" ) //-------------------------------------------------------------------------------------------------------- @@ -26,7 +26,7 @@ type certGetter interface { getCertificate(tlsConfig *tls.Config, address string) (*x509.Certificate, error) } -// Full TLS certificate fetcher +// Full TLS certificate fetcher. type fullTLSGetter struct{} func (f fullTLSGetter) getCertificate(tlsConfig *tls.Config, address string) (*x509.Certificate, error) { @@ -41,17 +41,18 @@ func (f fullTLSGetter) getCertificate(tlsConfig *tls.Config, address string) (*x return conn.ConnectionState().PeerCertificates[0], nil } -// SMTP+STARTTLS certificate getter +// SMTP+STARTTLS certificate getter. type smtpGetter struct{} -func (f smtpGetter) cmd(tcon *textproto.Conn, expectCode int, text string) (int, string, error) { +func (f smtpGetter) cmd(tcon *textproto.Conn, expectCode int, text string) error { id, err := tcon.Cmd("%s", text) if err != nil { - return 0, "", err + return err } tcon.StartResponse(id) defer tcon.EndResponse(id) - return tcon.ReadResponse(expectCode) + _, _, err = tcon.ReadResponse(expectCode) + return err } func (f smtpGetter) getCertificate(tlsConfig *tls.Config, address string) (*x509.Certificate, error) { @@ -64,10 +65,10 @@ func (f smtpGetter) getCertificate(tlsConfig *tls.Config, address string) (*x509 if _, _, err := text.ReadResponse(220); err != nil { return nil, err } - if _, _, err := f.cmd(text, 250, "HELO localhost"); err != nil { + if err := f.cmd(text, 250, "HELO localhost"); err != nil { return nil, err } - if _, _, err := f.cmd(text, 220, "STARTTLS"); err != nil { + if err := f.cmd(text, 220, "STARTTLS"); err != nil { return nil, err } t := tls.Client(conn, tlsConfig) @@ -77,9 +78,17 @@ func (f smtpGetter) getCertificate(tlsConfig *tls.Config, address string) (*x509 return t.ConnectionState().PeerCertificates[0], nil } -// ManageSieve STARTTLS certificate getter +// ManageSieve STARTTLS certificate getter. type sieveGetter struct{} +type sieveError struct { + msg string +} + +func (e sieveError) Error() string { + return "Sieve error: " + e.msg +} + func (f sieveGetter) waitOK(conn net.Conn) error { scanner := bufio.NewScanner(conn) for scanner.Scan() { @@ -88,10 +97,10 @@ func (f sieveGetter) waitOK(conn net.Conn) error { return nil } if strings.HasPrefix(line, "NO ") { - return errors.New(line[3:]) + return sieveError{msg: line[3:]} } if strings.HasPrefix(line, "BYE ") { - return errors.New(line[4:]) + return sieveError{msg: line[4:]} } } return scanner.Err() @@ -123,23 +132,23 @@ func (f sieveGetter) getCertificate(tlsConfig *tls.Config, address string) (*x50 return t.ConnectionState().PeerCertificates[0], nil } -// Supported StartTLS protocols -var certGetters map[string]certGetter = map[string]certGetter{ +// Supported StartTLS protocols. +var certGetters = map[string]certGetter{ "": fullTLSGetter{}, "smtp": &smtpGetter{}, "sieve": &sieveGetter{}, } -// Get a string that represents supported StartTLS protocols +// Get a string that represents supported StartTLS protocols. func listSupportedGetters() string { - sb := strings.Builder{} + strBuilder := strings.Builder{} for key := range certGetters { - if sb.Len() != 0 { - sb.WriteString(", ") + if strBuilder.Len() != 0 { + strBuilder.WriteString(", ") } - sb.WriteString(key) + strBuilder.WriteString(key) } - return sb.String() + return strBuilder.String() } //-------------------------------------------------------------------------------------------------------- @@ -227,7 +236,7 @@ func (program *checkProgram) CheckArguments() bool { return false } if _, ok := certGetters[program.startTLS]; !ok { - errstr := fmt.Sprintf("unsupported StartTLS protocol %s", program.startTLS) + errstr := "unsupported StartTLS protocol " + program.startTLS program.plugin.SetState(plugin.UNKNOWN, errstr) return false } @@ -239,6 +248,7 @@ func (program *checkProgram) CheckArguments() bool { // if connecting or performing the TLS handshake fail. func (program *checkProgram) getCertificate() error { tlsConfig := &tls.Config{ + //nolint:gosec // The whole point is to read the certificate. InsecureSkipVerify: true, MinVersion: tls.VersionTLS10, } @@ -273,7 +283,7 @@ func (program *checkProgram) checkHostName(name string) bool { return true } } - program.plugin.AddLine(fmt.Sprintf("missing DNS name %s in certificate", name)) + program.plugin.AddLine("missing DNS name " + name + " in certificate") return false } @@ -283,35 +293,41 @@ func (program *checkProgram) checkNames() bool { if len(program.certificate.DNSNames) == 0 { return program.checkSANlessCertificate() } - ok := program.checkHostName(program.hostname) + certificateIsOk := program.checkHostName(program.hostname) for _, name := range program.extraNames { - ok = program.checkHostName(name) && ok + certificateIsOk = program.checkHostName(name) && certificateIsOk } - if !ok { + if !certificateIsOk { program.plugin.SetState(plugin.CRITICAL, "names missing from SAN domain names") } - return ok + return certificateIsOk } -// Check a certificate's time to expiry agains the warning and critical +// Check a certificate's time to expiry against the warning and critical // thresholds, returning a status code and description based on these // values. func (program *checkProgram) checkCertificateExpiry(tlDays int) (plugin.Status, string) { if tlDays <= 0 { return plugin.CRITICAL, "certificate expired" } + var limitStr string var state plugin.Status - if program.crit > 0 && tlDays <= program.crit { + + switch { + case program.crit > 0 && tlDays <= program.crit: limitStr = fmt.Sprintf(" (<= %d)", program.crit) state = plugin.CRITICAL - } else if program.warn > 0 && tlDays <= program.warn { + + case program.warn > 0 && tlDays <= program.warn: limitStr = fmt.Sprintf(" (<= %d)", program.warn) state = plugin.WARNING - } else { + + default: limitStr = "" state = plugin.OK } + statusString := fmt.Sprintf("certificate will expire in %d days%s", tlDays, limitStr) return state, statusString @@ -320,12 +336,12 @@ func (program *checkProgram) checkCertificateExpiry(tlDays int) (plugin.Status, // Set the plugin's performance data based on the time left before the // certificate expires and the thresholds. func (program *checkProgram) setPerfData(tlDays int) { - pdat := perfdata.New("validity", perfdata.UOM_NONE, fmt.Sprintf("%d", tlDays)) + pdat := perfdata.New("validity", perfdata.UomNone, strconv.Itoa(tlDays)) if program.crit > 0 { - pdat.SetCrit(perfdata.PDRMax(fmt.Sprint(program.crit))) + pdat.SetCrit(perfdata.PDRMax(strconv.Itoa(program.crit))) } if program.warn > 0 { - pdat.SetWarn(perfdata.PDRMax(fmt.Sprint(program.warn))) + pdat.SetWarn(perfdata.PDRMax(strconv.Itoa(program.warn))) } program.plugin.AddPerfData(pdat) } diff --git a/cmd/zoneserial/main.go b/cmd/zoneserial/main.go index cfe5149..f00547a 100644 --- a/cmd/zoneserial/main.go +++ b/cmd/zoneserial/main.go @@ -5,15 +5,16 @@ import ( "net" "os" "reflect" + "strconv" "strings" "time" + "github.com/karrick/golf" + "github.com/miekg/dns" + "nocternity.net/gomonop/pkg/perfdata" "nocternity.net/gomonop/pkg/plugin" "nocternity.net/gomonop/pkg/program" - - "github.com/karrick/golf" - "github.com/miekg/dns" ) //------------------------------------------------------------------------------------------------------- @@ -33,7 +34,7 @@ type ( // Query a zone's SOA record through a given DNS and return the response using the channel. func queryZoneSOA(dnsq *dns.Msg, hostname string, port int, output responseChannel) { dnsc := new(dns.Client) - in, rtt, err := dnsc.Exchange(dnsq, net.JoinHostPort(hostname, fmt.Sprintf("%d", port))) + in, rtt, err := dnsc.Exchange(dnsq, net.JoinHostPort(hostname, strconv.Itoa(port))) output <- queryResponse{ data: in, rtt: rtt, @@ -88,7 +89,7 @@ func NewProgram() program.Program { func (program *checkProgram) Done() { if r := recover(); r != nil { program.plugin.SetState(plugin.UNKNOWN, "Internal error") - program.plugin.AddLine("Error info: %v", r) + program.plugin.AddLinef("Error info: %v", r) } program.plugin.Done() } @@ -131,7 +132,7 @@ func (program *checkProgram) queryServers() (queryResponse, queryResponse) { go queryZoneSOA(dnsq, program.hostname, program.port, checkOut) go queryZoneSOA(dnsq, program.rsHostname, program.rsPort, refOut) var checkResponse, refResponse queryResponse - for i := 0; i < 2; i++ { + for range 2 { select { case m := <-checkOut: checkResponse = m @@ -145,7 +146,7 @@ func (program *checkProgram) queryServers() (queryResponse, queryResponse) { // Add a server's RTT to the performance data. func (program *checkProgram) addRttPerf(name string, value time.Duration) { s := fmt.Sprintf("%f", value.Seconds()) - pd := perfdata.New(name, perfdata.UOM_SECONDS, s) + pd := perfdata.New(name, perfdata.UomSeconds, s) program.plugin.AddPerfData(pd) } @@ -155,20 +156,20 @@ func (program *checkProgram) addRttPerf(name string, value time.Duration) { // successful. func (program *checkProgram) getSerial(server string, response queryResponse) (ok bool, serial uint32) { if response.err != nil { - program.plugin.AddLine("%s server error : %s", server, response.err) + program.plugin.AddLinef("%s server error : %s", server, response.err) return false, 0 } - program.addRttPerf(fmt.Sprintf("%s_rtt", server), response.rtt) + program.addRttPerf(server+"_rtt", response.rtt) if len(response.data.Answer) != 1 { - program.plugin.AddLine("%s server did not return exactly one record", server) + program.plugin.AddLine(server + " server did not return exactly one record") return false, 0 } if soa, ok := response.data.Answer[0].(*dns.SOA); ok { - program.plugin.AddLine("serial on %s server: %d", server, soa.Serial) + program.plugin.AddLinef("serial on %s server: %d", server, soa.Serial) return true, soa.Serial } t := reflect.TypeOf(response.data.Answer[0]) - program.plugin.AddLine("%s server did not return SOA record; record type: %v", server, t) + program.plugin.AddLinef("%s server did not return SOA record; record type: %v", server, t) return false, 0 } diff --git a/main.go b/main.go index b669b15..e8ce79a 100644 --- a/main.go +++ b/main.go @@ -12,46 +12,49 @@ import ( ) var ( - programs map[string]program.ProgramBuilder = map[string]program.ProgramBuilder{ + programs map[string]program.Builder = map[string]program.Builder{ "check_ssl_certificate": sslcert.NewProgram, "check_zone_serial": zoneserial.NewProgram, } ) -func exitError(msg string, args ...interface{}) { - fmt.Fprintf(os.Stderr, msg+"\n", args...) - os.Exit(1) -} - -func main() { +func getProgram() program.Program { ownName := filepath.Base(os.Args[0]) - var program program.Program if builder, ok := programs[ownName]; ok { - program = builder() - } else if len(os.Args) < 2 { - exitError( - ""+ - "Syntax: %s [arguments]\n"+ - " %s --programs|-p\n"+ - " %s --version|-v", - os.Args[0], os.Args[0], os.Args[0]) - } else if os.Args[1] == "--programs" || os.Args[1] == "-p" { + return builder() + } + + if len(os.Args) < 2 { + fmt.Printf("Syntax: %s [arguments]\n", ownName) + fmt.Printf(" %s --programs|-p\n", ownName) + fmt.Printf(" %s --version|-v", ownName) + } + + switch os.Args[1] { + case "--programs", "-p": for name := range programs { fmt.Println(name) } os.Exit(0) - } else if os.Args[1] == "--version" || os.Args[1] == "-v" { - fmt.Printf("gomonop %s\n", version.Version()) - os.Exit(0) - } else if builder, ok := programs[os.Args[1]]; ok { - os.Args = os.Args[1:] - program = builder() - } else { - exitError("Unknown program: %s", os.Args[1]) + case "--version", "-v": + fmt.Printf("%s %s\n", ownName, version.Version()) + os.Exit(0) } + if builder, ok := programs[os.Args[1]]; ok { + os.Args = os.Args[1:] + return builder() + } + + fmt.Printf("Unknown program: %s\n", os.Args[1]) + os.Exit(1) + return nil +} + +func main() { + program := getProgram() defer program.Done() if program.CheckArguments() { program.RunCheck() diff --git a/pkg/perfdata/perfdata.go b/pkg/perfdata/perfdata.go index 9a5ca7c..bc7195b 100644 --- a/pkg/perfdata/perfdata.go +++ b/pkg/perfdata/perfdata.go @@ -12,15 +12,15 @@ import ( type UnitOfMeasurement int const ( - UOM_NONE UnitOfMeasurement = iota - UOM_SECONDS - UOM_PERCENT - UOM_BYTES - UOM_KILOBYTES - UOM_MEGABYTES - UOM_GIGABYTES - UOM_TERABYTES - UOM_COUNTER + UomNone UnitOfMeasurement = iota + UomSeconds + UomPercent + UomBytes + UomKilobytes + UomMegabytes + UomGigabytes + UomTerabytes + UomCounter ) func (u UnitOfMeasurement) String() string { @@ -31,24 +31,24 @@ func (u UnitOfMeasurement) String() string { type perfDataBits int const ( - PDAT_WARN perfDataBits = 1 << iota - PDAT_CRIT - PDAT_MIN - PDAT_MAX + PDatWarn perfDataBits = 1 << iota + PDatCrit + PDatMin + PDatMax ) // Regexps used to check values and ranges in performance data records. var ( - // Common value check regexp + // Common value check regexp. vcRegexp = `^-?(0(\.\d*)?|[1-9]\d*(\.\d*)?|\.\d+)$` - // Compiled value check regexp + // Compiled value check regexp. valueCheck = regexp.MustCompile(vcRegexp) - // Compiled range min value check + // Compiled range min value check. rangeMinCheck = regexp.MustCompile(vcRegexp + `|^~$`) ) -// Performance data range -type PerfDataRange struct { +// Performance data range. +type PDRange struct { start string end string inside bool @@ -56,54 +56,56 @@ type PerfDataRange struct { // Creates a performance data range from -inf to 0 and from the specified // value to +inf. -func PDRMax(max string) *PerfDataRange { +func PDRMax(max string) *PDRange { if !valueCheck.MatchString(max) { panic("invalid performance data range maximum value") } - r := &PerfDataRange{} - r.start = "0" - r.end = max - return r + pdRange := &PDRange{} + pdRange.start = "0" + pdRange.end = max + return pdRange } // Creates a performance data range from -inf to the specified minimal value // and from the specified maximal value to +inf. -func PDRMinMax(min, max string) *PerfDataRange { +func PDRMinMax(min, max string) *PDRange { if !valueCheck.MatchString(max) { panic("invalid performance data range maximum value") } if !rangeMinCheck.MatchString(min) { panic("invalid performance data range minimum value") } - r := &PerfDataRange{} - r.start = min - r.end = max - return r + pdRange := &PDRange{} + pdRange.start = min + pdRange.end = max + return pdRange } // Inverts the range. -func (r *PerfDataRange) Inside() *PerfDataRange { +func (r *PDRange) Inside() *PDRange { r.inside = true return r } // Generates the range's string representation so it can be sent to the // monitoring system. -func (r *PerfDataRange) String() string { +func (r *PDRange) String() string { var start, inside string - if r.start == "" { + + switch r.start { + case "": start = "~" - } else if r.start == "0" { + case "0": start = "" - } else { + default: start = r.start } + if r.inside { inside = "@" - } else { - inside = "" } - return fmt.Sprintf("%s%s:%s", inside, start, r.end) + + return inside + start + ":" + r.end } // Performance data, including a label, units, a value, warning/critical @@ -113,7 +115,7 @@ type PerfData struct { units UnitOfMeasurement bits perfDataBits value string - warn, crit PerfDataRange + warn, crit PDRange min, max string } @@ -122,36 +124,36 @@ func New(label string, units UnitOfMeasurement, value string) *PerfData { if value != "" && !valueCheck.MatchString(value) { panic("invalid value") } - r := &PerfData{} - r.Label = label - r.units = units + pdRange := &PerfData{} + pdRange.Label = label + pdRange.units = units if value == "" { - r.value = "U" + pdRange.value = "U" } else { - r.value = value + pdRange.value = value } - return r + return pdRange } // Set the warning range for the performance data record. -func (d *PerfData) SetWarn(r *PerfDataRange) { +func (d *PerfData) SetWarn(r *PDRange) { d.warn = *r - d.bits = d.bits | PDAT_WARN + d.bits |= PDatWarn } // Set the critical range for the performance data record. -func (d *PerfData) SetCrit(r *PerfDataRange) { +func (d *PerfData) SetCrit(r *PDRange) { d.crit = *r - d.bits = d.bits | PDAT_CRIT + d.bits |= PDatCrit } -// Set the performance data's minimal value +// Set the performance data's minimal value. func (d *PerfData) SetMin(min string) { if !valueCheck.MatchString(min) { panic("invalid value") } d.min = min - d.bits = d.bits | PDAT_MIN + d.bits |= PDatMin } // Set the performance data's maximal value. @@ -160,38 +162,38 @@ func (d *PerfData) SetMax(max string) { panic("invalid value") } d.max = max - d.bits = d.bits | PDAT_MAX + d.bits |= PDatMax } // Converts performance data to a string which may be read by the monitoring // system. func (d *PerfData) String() string { - var sb strings.Builder + var strBuilder strings.Builder needsQuotes := strings.ContainsAny(d.Label, " '=\"") if needsQuotes { - sb.WriteString("'") + strBuilder.WriteString("'") } - sb.WriteString(strings.ReplaceAll(d.Label, "'", "''")) + strBuilder.WriteString(strings.ReplaceAll(d.Label, "'", "''")) if needsQuotes { - sb.WriteString("'") + strBuilder.WriteString("'") } - sb.WriteString("=") - sb.WriteString(fmt.Sprintf("%s%s;", d.value, d.units.String())) - if d.bits&PDAT_WARN != 0 { - sb.WriteString(d.warn.String()) + strBuilder.WriteString("=") + strBuilder.WriteString(fmt.Sprintf("%s%s;", d.value, d.units.String())) + if d.bits&PDatWarn != 0 { + strBuilder.WriteString(d.warn.String()) } - sb.WriteString(";") - if d.bits&PDAT_CRIT != 0 { - sb.WriteString(d.crit.String()) + strBuilder.WriteString(";") + if d.bits&PDatCrit != 0 { + strBuilder.WriteString(d.crit.String()) } - sb.WriteString(";") - if d.bits&PDAT_MIN != 0 { - sb.WriteString(d.min) + strBuilder.WriteString(";") + if d.bits&PDatMin != 0 { + strBuilder.WriteString(d.min) } - sb.WriteString(";") - if d.bits&PDAT_MAX != 0 { - sb.WriteString(d.max) + strBuilder.WriteString(";") + if d.bits&PDatMax != 0 { + strBuilder.WriteString(d.max) } - return sb.String() + return strBuilder.String() } diff --git a/pkg/perfdata/perfdata_test.go b/pkg/perfdata/perfdata_test.go index f86bd05..0c0fe2f 100644 --- a/pkg/perfdata/perfdata_test.go +++ b/pkg/perfdata/perfdata_test.go @@ -78,7 +78,7 @@ func TestPdrMinMax(t *testing.T) { } func TestPdrInside(t *testing.T) { - pdr := &PerfDataRange{} + pdr := &PDRange{} pdr = pdr.Inside() assert(t, pdr.inside, "Inside flag should be set") pdr = pdr.Inside() @@ -87,14 +87,14 @@ func TestPdrInside(t *testing.T) { func TestPdrString(t *testing.T) { type Test struct { - pdr PerfDataRange + pdr PDRange out string } tests := []Test{ - {pdr: PerfDataRange{start: "Y", end: "X"}, out: "Y:X"}, - {pdr: PerfDataRange{end: "X"}, out: "~:X"}, - {pdr: PerfDataRange{start: "0", end: "X"}, out: ":X"}, - {pdr: PerfDataRange{inside: true, start: "Y", end: "X"}, out: "@Y:X"}, + {pdr: PDRange{start: "Y", end: "X"}, out: "Y:X"}, + {pdr: PDRange{end: "X"}, out: "~:X"}, + {pdr: PDRange{start: "0", end: "X"}, out: ":X"}, + {pdr: PDRange{inside: true, start: "Y", end: "X"}, out: "@Y:X"}, } for _, test := range tests { diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index ab4db13..f32e360 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -58,17 +58,22 @@ func (p *Plugin) SetState(status Status, message string) { } // AddLine adds the specified string to the extra output text buffer. -func (p *Plugin) AddLine(format string, data ...interface{}) { +func (p *Plugin) AddLine(line string) { if p.extraText == nil { p.extraText = list.New() } - p.extraText.PushBack(fmt.Sprintf(format, data...)) + p.extraText.PushBack(line) +} + +// AddLinef formats the input and adds it to the text buffer. +func (p *Plugin) AddLinef(format string, data ...interface{}) { + p.AddLine(fmt.Sprintf(format, data...)) } // AddLines add the specified `lines` to the output text. func (p *Plugin) AddLines(lines []string) { for _, line := range lines { - p.AddLine("%s", line) + p.AddLine(line) } } @@ -78,7 +83,7 @@ func (p *Plugin) AddLines(lines []string) { func (p *Plugin) AddPerfData(pd *perfdata.PerfData) { _, exists := p.perfData[pd.Label] if exists { - panic(fmt.Sprintf("duplicate performance data %s", pd.Label)) + panic("duplicate performance data " + pd.Label) } p.perfData[pd.Label] = pd } @@ -87,30 +92,31 @@ func (p *Plugin) AddPerfData(pd *perfdata.PerfData) { // and performance data, before exiting with the code corresponding to the // status. func (p *Plugin) Done() { - var sb strings.Builder - sb.WriteString(p.name) - sb.WriteString(" ") - sb.WriteString(p.status.String()) - sb.WriteString(": ") - sb.WriteString(p.message) + var strBuilder strings.Builder + strBuilder.WriteString(p.name) + strBuilder.WriteString(" ") + strBuilder.WriteString(p.status.String()) + strBuilder.WriteString(": ") + strBuilder.WriteString(p.message) if len(p.perfData) > 0 { - sb.WriteString(" | ") + strBuilder.WriteString(" | ") needSep := false - for k := range p.perfData { + for _, data := range p.perfData { if needSep { - sb.WriteString(", ") + strBuilder.WriteString(", ") } else { needSep = true } - sb.WriteString(p.perfData[k].String()) + strBuilder.WriteString(data.String()) } } if p.extraText != nil { for em := p.extraText.Front(); em != nil; em = em.Next() { - sb.WriteString("\n") - sb.WriteString(em.Value.(string)) + strBuilder.WriteString("\n") + //nolint:forcetypeassert // we want to panic if this isn't a string + strBuilder.WriteString(em.Value.(string)) } } - fmt.Println(sb.String()) + fmt.Println(strBuilder.String()) os.Exit(int(p.status)) } diff --git a/pkg/program/program.go b/pkg/program/program.go index 980c8dd..d8b3510 100644 --- a/pkg/program/program.go +++ b/pkg/program/program.go @@ -6,4 +6,4 @@ type Program interface { Done() } -type ProgramBuilder func() Program +type Builder func() Program