Reviewed-on: #33 Co-authored-by: Natercio Moniz <[email protected]>
This commit was merged in pull request #33.
This commit is contained in:
1 parent
6c989c338c
commit
47f38fce2b
20 files changed
+782
-315
No files matched your search
@@ -0,0 +1,85 @@
|
||||
// Package html renders an internal.AggregatorWriter as a complete,
|
||||
// standalone HTML document: the report table fragment (built from the
|
||||
// shared table definition in internal/writing/internal/tabledef, so it can
|
||||
// never drift from the terminal layout) embedded in a Bootstrap-styled page
|
||||
// with a translated title and generation timestamp. The official Bootstrap
|
||||
// 5.3.3 stylesheet is inlined via go:embed, so the document renders fully
|
||||
// offline.
|
||||
package html
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/nmoniz/any2anexoj/internal"
|
||||
"github.com/nmoniz/any2anexoj/internal/writing/internal/tabledef"
|
||||
)
|
||||
|
||||
// bootstrapMinCSS is the official Bootstrap 5.3.3 minified CSS distribution
|
||||
// file, embedded so every rendered document renders fully offline. After any
|
||||
// version bump, re-verify provenance with:
|
||||
//
|
||||
// openssl dgst -sha384 -binary bootstrap.min.css | base64
|
||||
//
|
||||
// It must print the official SRI hash published at
|
||||
// https://getbootstrap.com/docs/5.3/getting-started/download/
|
||||
// (sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH).
|
||||
//
|
||||
//go:embed bootstrap.min.css
|
||||
var bootstrapMinCSS string
|
||||
|
||||
// Writer writes a complete HTML document for an AggregatorWriter.
|
||||
type Writer struct {
|
||||
output io.Writer
|
||||
translator tabledef.Translator
|
||||
lang string
|
||||
}
|
||||
|
||||
// NewWriter builds a Writer that writes a complete HTML document to w. The
|
||||
// report table is rendered from the shared table definition via tr, and the
|
||||
// document title is looked up from the "report_title" translation via tr,
|
||||
// so callers never pass a pre-baked title.
|
||||
func NewWriter(w io.Writer, tr tabledef.Translator, lang string) *Writer {
|
||||
return &Writer{
|
||||
output: w,
|
||||
translator: tr,
|
||||
lang: lang,
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed document.tmpl
|
||||
var documentTemplateBody string
|
||||
|
||||
// documentTemplate renders the full HTML page from document.tmpl. The table
|
||||
// fragment is typed as template.HTML so the template engine emits it
|
||||
// verbatim without escaping the angle brackets in the <table> markup.
|
||||
var documentTemplate = template.Must(template.New("html").Parse(documentTemplateBody))
|
||||
|
||||
// Render writes a complete standalone HTML document for aw to the configured
|
||||
// output. The same Writer may be reused; each call rebuilds the table
|
||||
// fragment from aw so the rendered <tr> count reflects aw's current items and
|
||||
// no rows are duplicated across invocations.
|
||||
func (hw *Writer) Render(aw *internal.AggregatorWriter) error {
|
||||
fragment := hw.renderTableFragment(aw)
|
||||
title := hw.translator.Translate("report_title", 1, nil)
|
||||
data := struct {
|
||||
Lang string
|
||||
Title string
|
||||
CSS template.CSS
|
||||
TableFragment template.HTML
|
||||
GeneratedAt string
|
||||
}{
|
||||
Lang: hw.lang,
|
||||
Title: title,
|
||||
CSS: template.CSS(bootstrapMinCSS),
|
||||
TableFragment: template.HTML(fragment),
|
||||
GeneratedAt: time.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
if err := documentTemplate.Execute(hw.output, data); err != nil {
|
||||
return fmt.Errorf("render html document: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user