Skip to content

Commit

Permalink
New kit theme (#40)
Browse files Browse the repository at this point in the history
Fixes #28
  • Loading branch information
matm authored Mar 8, 2023
1 parent 2b78593 commit fb5bbdb
Show file tree
Hide file tree
Showing 17 changed files with 413 additions and 62 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
v 1.3
- New kit theme. #28
- refac: generate Go code to render themes. #38
- doc: fix semver in README. #36

Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
# Gocov HTML export

This is a simple helper tool for generating HTML output from [axw/gocov](https://github.com/axw/gocov/)
This is a simple helper tool for generating HTML output from [axw/gocov](https://github.com/axw/gocov/).

Here is a screenshot:

![HTML coverage report screenshot](https://github.com/matm/gocov-html/blob/master/gocovh-html.png)
`gocov-html` has support for themes, you might want to have a [look at the screenshots](themes/README.md).

## Installation

Binaries for most amd64 systems are built for every release. Please just [grab a binary version of the latest release](https://github.com/matm/gocov-html/releases).

However you can build it from source. Please note that building from source requires Go 1.11+.
You can also build it from source. In this case, a working Go 1.11+ compiler is required.

Just type the following to install the program and its dependencies:
The easiest way to compile this tool is to fetch the repository's code and run `make` (which defaults to using the `build` target):
```bash
$ go install github.com/axw/gocov/gocov@latest
$ go install github.com/matm/gocov-html/cmd/gocov-html@latest
$ git clone https://github.com/matm/gocov-html.git
$ cd gocov-html
$ make
```

## Features Matrix
Expand All @@ -28,6 +27,7 @@ Write CSS of default theme to stdout|`-d`|`1.2.0`
Embbed custom CSS into final HTML document|-|`1.2.0`
List available themes|`-lt`|`1.2.0`
Render with a specific theme|`-t <theme>`|`1.2.0`
New `kit` theme |`-t kit`|`1.3.0`

## Usage

Expand Down
7 changes: 4 additions & 3 deletions build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ cleardist:
@rm -rf ${DISTDIR} && mkdir -p ${BINDIR} && mkdir -p ${BUILDDIR}

build:
@go build ${GENERATOR_CMD}
@go generate ./...
@go build -ldflags "all=$(GO_LDFLAGS)" ${MAIN_CMD}
go build ${GENERATOR_CMD} && \
go generate ./...
@echo "building ..." && \
go build -ldflags "all=$(GO_LDFLAGS)" ${MAIN_CMD}

test:
@go test ./...
Expand Down
89 changes: 54 additions & 35 deletions cmd/generator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,41 @@ import (
"github.com/matm/gocov-html/pkg/types"
)
func (t defaultTheme) Data() *types.TemplateData {
func (t {{.Type}}) Data() *types.TemplateData {
td:= &types.TemplateData{
When: time.Now().Format(time.RFC822Z),
When: time.Now().Format(time.RFC1123),
ProjectURL: types.ProjectURL,
}
{{if .Style}}
td.Style = {{.Style}}
td.Style = string({{.Style}})
{{end}}
{{if .Script}}
td.Script = {{.Script}}
td.Script = string({{.Script}})
{{end}}
return td
}
func (t defaultTheme) Template() *template.Template {
func (t {{.Type}}) Template() *template.Template {
tmpl := {{.Template}}
p := template.Must(template.New("theme").Parse(tmpl))
return p
}
`

func inspect(name string, theme *string, assets *types.StaticAssets) error {
func inspect(p *params) error {
fset := token.NewFileSet()
token, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
token, err := parser.ParseFile(fset, p.filename, nil, parser.ParseComments)
if err != nil {
return err
}
ast.Inspect(token, func(n ast.Node) bool {
fn, ok := n.(*ast.FuncDecl)
if ok {
p.rtype = fn.Recv.List[0].Type.(*ast.Ident).Name
switch fn.Name.Name {
case "Name":
*theme = fn.Body.List[0].(*ast.ReturnStmt).Results[0].(*ast.BasicLit).Value
*theme = strings.Replace(*theme, `"`, "", -1)
p.theme = fn.Body.List[0].(*ast.ReturnStmt).Results[0].(*ast.BasicLit).Value
p.theme = strings.Replace(p.theme, `"`, "", -1)
case "Assets":
es := fn.Body.List[0].(*ast.ReturnStmt).Results[0].(*ast.CompositeLit).Elts
for _, e := range es {
Expand All @@ -71,16 +72,16 @@ func inspect(name string, theme *string, assets *types.StaticAssets) error {
elems := kv.Value.(*ast.CompositeLit).Elts
for _, elem := range elems {
sheet := elem.(*ast.BasicLit).Value
assets.Stylesheets = append(assets.Stylesheets, strings.Replace(sheet, `"`, "", -1))
p.assets.Stylesheets = append(p.assets.Stylesheets, strings.Replace(sheet, `"`, "", -1))
}
case "Index":
tmplName := kv.Value.(*ast.BasicLit).Value
assets.Index = strings.Replace(tmplName, `"`, "", -1)
p.assets.Index = strings.Replace(tmplName, `"`, "", -1)
case "Scripts":
elems := kv.Value.(*ast.CompositeLit).Elts
for _, elem := range elems {
script := elem.(*ast.BasicLit).Value
assets.Scripts = append(assets.Scripts, strings.Replace(script, `"`, "", -1))
p.assets.Scripts = append(p.assets.Scripts, strings.Replace(script, `"`, "", -1))
}
}
}
Expand All @@ -92,36 +93,46 @@ func inspect(name string, theme *string, assets *types.StaticAssets) error {
return nil
}

func render(name, theme string, assets types.StaticAssets) error {
baseThemeDir := path.Join("..", "..", "themes", theme)
out := strings.Replace(name, ".go", "_gen.go", 1)
func render(p *params) error {
baseThemeDir := path.Join("..", "..", "themes", p.theme)
out := strings.Replace(p.filename, ".go", "_gen.go", 1)
outFile, err := os.Create(out)
if err != nil {
return err
}
defer outFile.Close()
index, err := ioutil.ReadFile(path.Join(baseThemeDir, assets.Index))
index, err := ioutil.ReadFile(path.Join(baseThemeDir, p.assets.Index))
if err != nil {
return err
}
// Contains all stylesheets' data.
var allStyles bytes.Buffer
for _, css := range assets.Stylesheets {
style, err := ioutil.ReadFile(path.Join(baseThemeDir, css))
if err != nil {
return err
}
fmt.Fprintf(&allStyles, "`%s`", style)
}

// Contains all scripts' data.
var allScripts bytes.Buffer
for _, script := range assets.Scripts {
js, err := ioutil.ReadFile(path.Join(baseThemeDir, script))
if err != nil {
return err

type static struct {
buf *bytes.Buffer
assets []string
}

for _, st := range []static{
{&allStyles, p.assets.Stylesheets},
{&allScripts, p.assets.Scripts},
} {
var buf bytes.Buffer
for _, asset := range st.assets {
raw, err := ioutil.ReadFile(path.Join(baseThemeDir, asset))
if err != nil {
return err
}
_, err = buf.Write(raw)
if err != nil {
return err
}
}
fmt.Fprintf(&allScripts, "`%s`", js)
// Write a slice of bytes to deal with any invalid character.
// Later converted to a string before template rendering.
fmt.Fprintf(st.buf, "%#v", buf.Bytes())
}
t, err := template.New("").Parse(tmpl)
if err != nil {
Expand All @@ -131,28 +142,36 @@ func render(name, theme string, assets types.StaticAssets) error {
Script string
Style string
Template string
Type string
}
err = t.Execute(outFile, &data{
Script: allScripts.String(),
Style: allStyles.String(),
Template: "`" + string(index) + "`"},
)
Template: "`" + string(index) + "`",
Type: p.rtype,
})
return err
}

type params struct {
filename string
rtype string // Receiver type.
theme string
assets types.StaticAssets
}

func main() {
name := os.Getenv("GOFILE")
if name == "" {
fmt.Println("Must be run by the \"go generate\" tool, like \"go generate ./...\"")
os.Exit(1)
}
assets := new(types.StaticAssets)
theme := new(string)
err := inspect(name, theme, assets)
p := &params{filename: name}
err := inspect(p)
if err != nil {
log.Fatal(err)
}
if err := render(name, *theme, *assets); err != nil {
if err := render(p); err != nil {
log.Fatal(err)
}
}
7 changes: 7 additions & 0 deletions pkg/cov/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"io/ioutil"
"os"
"sort"
"strings"
"time"

"github.com/axw/gocov"
"github.com/matm/gocov-html/pkg/themes"
Expand Down Expand Up @@ -120,12 +122,15 @@ func printReport(w io.Writer, r *report) error {
css = string(style)
}
reportPackages := make(types.ReportPackageList, len(r.packages))
pkgNames := make([]string, len(r.packages))
for i, pkg := range r.packages {
reportPackages[i] = buildReportPackage(pkg)
pkgNames[i] = pkg.Name
}

data.Style = css
data.Packages = reportPackages
data.Command = fmt.Sprintf("gocov test %s | gocov-html -t %s", strings.Join(pkgNames, " "), theme.Name())

if len(reportPackages) > 1 {
rv := types.ReportPackage{
Expand Down Expand Up @@ -153,6 +158,7 @@ func exists(path string) (bool, error) {
// is an absolute path to a custom stylesheet. Use an empty
// string to use the default stylesheet available.
func HTMLReportCoverage(r io.Reader, css string) error {
t0 := time.Now()
report := newReport()

// Custom stylesheet?
Expand Down Expand Up @@ -180,5 +186,6 @@ func HTMLReportCoverage(r io.Reader, css string) error {
}
fmt.Println()
err = printReport(os.Stdout, report)
fmt.Fprintf(os.Stderr, "Took %v\n", time.Since(t0))
return eris.Wrap(err, "HTML report")
}
31 changes: 31 additions & 0 deletions pkg/themes/kit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package themes

//go:generate ../../generator

import (
"github.com/matm/gocov-html/pkg/types"
)

type kitTheme struct{}

func (t kitTheme) Assets() types.StaticAssets {
return types.StaticAssets{
Stylesheets: []string{
// From the official theme.
"app.css",
//"a.css", "b.css",
// Custom rules.
"kit.css",
},
Scripts: []string{"app.js"},
Index: "index.html",
}
}

func (t kitTheme) Name() string {
return "kit"
}

func (t kitTheme) Description() string {
return "AdminKit theme"
}
1 change: 1 addition & 0 deletions pkg/themes/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

var themes = []types.Beautifier{
defaultTheme{},
kitTheme{},
}

// Theme to use for rendering.
Expand Down
2 changes: 2 additions & 0 deletions pkg/types/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type Beautifier interface {

// TemplateData has all the fields needed by the the HTML template for rendering.
type TemplateData struct {
// Command is the shell command used to generate the HTML report.
Command string
// Style is the stylesheet content that will be embedded in the HTML page.
Style string
// Script is the javascript content that will be embedded in the HTML page.
Expand Down
21 changes: 21 additions & 0 deletions themes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Available Themes

## golang

This is the default theme:

```shell
$ gocov test strings | gocov-html -t golang > strings.html
```

![golang theme screenshot](golang/screenshot.png)

## kit

Uses the [AdminKit](https://adminkit.io) theme:

```shell
$ gocov test encoding/csv strings | gocov-html -t kit > strings.html
```

![kit theme screenshot](kit/screenshot.png)
Loading

0 comments on commit fb5bbdb

Please sign in to comment.