From 91baea3252b2a8f215b7eff018e22378ae85dd8c Mon Sep 17 00:00:00 2001 From: oval Date: Tue, 7 Jul 2026 16:57:38 +0000 Subject: [PATCH] Add reports/render.go --- reports/render.go | 101 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 reports/render.go diff --git a/reports/render.go b/reports/render.go new file mode 100644 index 0000000..c501a60 --- /dev/null +++ b/reports/render.go @@ -0,0 +1,101 @@ +// render.go — Go-based IEC 62443-3-3 SL2 compliance report renderer. +// +// Loads a JSON report produced by ansible-test and renders it through +// an external Go template file (report.gohtml by default). +// +// Usage: +// go run render.go [template.gohtml] +// +// The template receives the full JSON document as an untyped map. +// Registered template functions: +// passIcon — maps passed value to ✅/❌/🔍/❓ +// severityIcon — maps severity string to 🔴/🟠/🟡/🟢/⚪ +// title — capitalizes first letter of each word +// divf — float64 division (a/b*100) for percentages +// add, sub — integer arithmetic +// +// Dependencies: +// golang.org/x/text v0.14.0 (for cases.Title) + +package main + +import ( + "encoding/json" + "fmt" + "os" + "text/template" + + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +func main() { + if len(os.Args) < 2 { + fmt.Fprintf(os.Stderr, "Usage: gomplate-report [template.gohtml]\n") + os.Exit(1) + } + + jsonPath := os.Args[1] + tmplPath := "reports/report.gohtml" + if len(os.Args) >= 3 { + tmplPath = os.Args[2] + } + + data, err := os.ReadFile(jsonPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading JSON: %v\n", err) + os.Exit(1) + } + + var report map[string]any + if err := json.Unmarshal(data, &report); err != nil { + fmt.Fprintf(os.Stderr, "Error parsing JSON: %v\n", err) + os.Exit(1) + } + + funcMap := template.FuncMap{ + "passIcon": func(v any) string { + b, ok := v.(bool) + if !ok { + return "❓ MANUAL" + } + if b { + return "✅ PASS" + } + return "❌ FAIL" + }, + "severityIcon": func(s string) string { + switch s { + case "critical": + return "🔴" + case "high": + return "🟠" + case "medium": + return "🟡" + case "low": + return "🟢" + } + return "⚪" + }, + "title": cases.Title(language.English).String, + "divf": func(a, b int) float64 { + if b == 0 { + return 0 + } + return float64(a) / float64(b) * 100.0 + }, + "add": func(a, b int) int { return a + b }, + "sub": func(a, b int) int { return a - b }, + } + + tmpl, err := template.New("report.gohtml").Funcs(funcMap).ParseFiles(tmplPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Error parsing template: %v\n", err) + os.Exit(1) + } + + if err := tmpl.ExecuteTemplate(os.Stdout, "report.gohtml", report); err != nil { + fmt.Fprintf(os.Stderr, "Error rendering: %v\n", err) + os.Exit(1) + } +}