package html
import (
"bufio"
"fmt"
"html/template"
"io"
"math"
"os"
"strings"
"github.com/IcedElect/goverage/internal/coverage"
"github.com/IcedElect/goverage/internal/structure/elements"
"github.com/IcedElect/goverage/internal/structure/files"
"github.com/IcedElect/goverage/internal/structure/tree"
"golang.org/x/tools/cover"
)
func renderDirectory(w io.Writer, dir *tree.Directory, coverage coverage.Coverage, elements []*elements.Element) error {
tmplParsed, err := template.New("layout").
Funcs(templateFuncs).
ParseFS(templates, "templates/layout.html", "templates/directory.page.html")
if err != nil {
return fmt.Errorf("error parsing templates: %w", err)
}
dirPath := ""
if dir != nil {
dirPath = dir.Path
}
err = tmplParsed.Execute(w, TemplateData{
CurrentPath: dirPath,
Global: globalData,
Directory: dir,
Coverage: coverage,
Elements: elements,
})
if err != nil {
return fmt.Errorf("error executing template: %w", err)
}
return nil
}
func renderFile(w io.Writer, file *files.File, coverage coverage.Coverage) error {
tmplParsed, err := template.New("layout").
Funcs(templateFuncs).
ParseFS(templates, "templates/layout.html", "templates/file.page.html")
if err != nil {
return fmt.Errorf("error parsing templates: %w", err)
}
code, err := getFileCode(file.Path, file.Profile)
if err != nil {
return fmt.Errorf("error generating code HTML for %s: %w", file.Path, err)
}
err = tmplParsed.Execute(w, TemplateData{
CurrentPath: file.RelativePath,
Global: globalData,
File: file,
FileCode: template.HTML(code),
Coverage: coverage,
})
if err != nil {
return fmt.Errorf("error executing template: %w", err)
}
return nil
}
func getFileCode(path string, profile *cover.Profile) (string, error) {
src, err := os.ReadFile(path)
if err != nil {
return "", fmt.Errorf("can't read %q: %w", path, err)
}
var buf strings.Builder
boundaries := profile.Boundaries(src)
err = htmlGen(&buf, src, boundaries)
if err != nil {
return "", fmt.Errorf("error generating HTML for %s: %w", path, err)
}
return buf.String(), nil
}
// htmlGen generates an HTML coverage report with the provided filename,
// source code, and tokens, and writes it to the given Writer.
func htmlGen(w io.Writer, src []byte, boundaries []cover.Boundary) error {
dst := bufio.NewWriter(w)
for i := range src {
for len(boundaries) > 0 && boundaries[0].Offset == i {
b := boundaries[0]
if b.Start {
n := 0
if b.Count > 0 {
n = int(math.Floor(b.Norm*9)) + 1
}
fmt.Fprintf(dst, `<span class="cov%v" title="%v">`, n, b.Count)
} else {
_, _ = dst.WriteString("</span>")
}
boundaries = boundaries[1:]
}
switch b := src[i]; b {
case '>':
_, _ = dst.WriteString(">")
case '<':
_, _ = dst.WriteString("<")
case '&':
_, _ = dst.WriteString("&")
case '\t':
_, _ = dst.WriteString(" ")
default:
_ = dst.WriteByte(b)
}
}
return dst.Flush()
}