-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rendering): first draft of HTML5 rendering (#3)
using the `html/template` package to produce the HTML output from a given array of `types.DocElement`. Signed-off-by: Xavier Coulon <[email protected]>
- Loading branch information
Showing
2 changed files
with
159 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package html5 | ||
|
||
import ( | ||
"context" | ||
"html/template" | ||
"io" | ||
|
||
"reflect" | ||
|
||
"bytes" | ||
|
||
"github.com/bytesparadise/libasciidoc/types" | ||
"github.com/pkg/errors" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
var stringTemplate *template.Template | ||
var inlineContentTemplate *template.Template | ||
var boldContentTemplate *template.Template | ||
|
||
// initializes the templates | ||
func init() { | ||
var err error | ||
inlineContentTemplate, err = template.New("inlineContent").Parse("<div class=\"paragraph\">\n<p>{{.}}</p>\n</div>") | ||
if err != nil { | ||
log.Fatalf("failed to initialize HTML template: %s", err.Error()) | ||
} | ||
stringTemplate, err = template.New("string").Parse("{{.}}") | ||
if err != nil { | ||
log.Fatalf("failed to initialize HTML template: %s", err.Error()) | ||
} | ||
boldContentTemplate, err = template.New("boldQuote").Parse("<strong>{{.}}</strong>") | ||
if err != nil { | ||
log.Fatalf("failed to initialize HTML template: %s", err.Error()) | ||
} | ||
} | ||
|
||
// RenderToString renders the givem `document` in HTML and returns the result as a `string` | ||
func RenderToString(ctx context.Context, document types.Document) (*string, error) { | ||
buff := bytes.NewBuffer(make([]byte, 0)) | ||
err := RenderToWriter(ctx, document, buff) | ||
if err != nil { | ||
return nil, err | ||
} | ||
result := string(buff.Bytes()) | ||
return &result, nil | ||
} | ||
|
||
// RenderToWriter renders the givem `document` in HTML and writes the result in the given `writer` | ||
func RenderToWriter(ctx context.Context, document types.Document, writer io.Writer) error { | ||
for _, element := range document.Elements { | ||
renderedElement, err := renderDocElement(element) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to render document") | ||
} | ||
_, err = writer.Write(renderedElement) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to render document") | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func renderDocElement(docElement types.DocElement) ([]byte, error) { | ||
switch docElement.(type) { | ||
case *types.InlineContent: | ||
return renderInlineContent(*docElement.(*types.InlineContent)) | ||
case *types.BoldQuote: | ||
return renderBoldQuote(*docElement.(*types.BoldQuote)) | ||
case *types.StringElement: | ||
return renderStringElement(*docElement.(*types.StringElement)) | ||
default: | ||
return nil, errors.Errorf("unsupported element type: %v", reflect.TypeOf(docElement)) | ||
} | ||
|
||
} | ||
|
||
func renderInlineContent(inlineContent types.InlineContent) ([]byte, error) { | ||
renderedElementsBuff := bytes.NewBuffer(make([]byte, 0)) | ||
for _, element := range inlineContent.Elements { | ||
renderedElement, err := renderDocElement(element) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "unable to render inline content element") | ||
} | ||
renderedElementsBuff.Write(renderedElement) | ||
} | ||
result := bytes.NewBuffer(make([]byte, 0)) | ||
// here we must preserve the HTML tags | ||
err := inlineContentTemplate.Execute(result, template.HTML(renderedElementsBuff.String())) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "unable to render inline content") | ||
} | ||
log.Debugf("rendered inline content: %s", result.Bytes()) | ||
return result.Bytes(), nil | ||
} | ||
|
||
func renderBoldQuote(q types.BoldQuote) ([]byte, error) { | ||
result := bytes.NewBuffer(make([]byte, 0)) | ||
err := boldContentTemplate.Execute(result, q.Content) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "unable to render bold quote") | ||
} | ||
log.Debugf("rendered bold quote: %s", result.Bytes()) | ||
return result.Bytes(), nil | ||
} | ||
|
||
func renderStringElement(s types.StringElement) ([]byte, error) { | ||
result := bytes.NewBuffer(make([]byte, 0)) | ||
err := stringTemplate.Execute(result, s.Content) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "unable to render string element") | ||
} | ||
log.Debugf("rendered string: %s", result.Bytes()) | ||
return result.Bytes(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package html5_test | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"context" | ||
|
||
"github.com/bytesparadise/libasciidoc/parser" | ||
. "github.com/bytesparadise/libasciidoc/renderer/html5" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestHtml5Renderer(t *testing.T) { | ||
|
||
t.Run("bold content alone", func(t *testing.T) { | ||
// given | ||
content := "*bold content*" | ||
expected := `<div class="paragraph"><p><strong>bold content</strong></p></div>` | ||
verify(t, expected, content) | ||
}) | ||
|
||
t.Run("bold content in sentence", func(t *testing.T) { | ||
// given | ||
content := "some *bold content*." | ||
expected := `<div class="paragraph"><p>some <strong>bold content</strong>.</p></div>` | ||
verify(t, expected, content) | ||
}) | ||
} | ||
|
||
func verify(t *testing.T, expected, content string) { | ||
document, err := parser.ParseString(content) | ||
require.Nil(t, err) | ||
// when | ||
actual, errs := RenderToString(context.Background(), *document) | ||
// then | ||
require.Nil(t, errs) | ||
assert.Equal(t, singleLine(expected), singleLine(*actual)) | ||
} | ||
|
||
func singleLine(content string) string { | ||
return strings.Replace(content, "\n", "", -1) | ||
} |