Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): support attributes set/reset in CLI #495

Merged
merged 1 commit into from
Mar 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,21 @@ Libasciidoc provides 2 functions to convert an Asciidoc content into HTML:

1. Converting an `io.Reader` into an HTML document:

func ConvertToHTML(ctx context.Context, source io.Reader, output io.Writer, options renderer.Option...) (map[string]interface{}, error)
ConvertToHTML(r io.Reader, output io.Writer, config configuration.Configuration) (types.Metadata, error)

2. Converting a file (given its name) into an HTML document:

func ConvertFileToHTML(ctx context.Context, filename string, output io.Writer, options renderer.Option...) (map[string]interface{}, error)
ConvertFileToHTML(output io.Writer, config configuration.Configuration) (types.Metadata, error)

where the returned `map[string]interface{}` object contains the document's title which is not part of the generated HTML `<body>` part, as well as the other attributes of the source document.
where the returned `types.Metadata` object contains the document's title which is not part of the generated HTML `<body>` part, as well as the other attributes of the source document.

For now, the sole option to pass as a last argument is `renderer.IncludeHeaderFooter` to include the `<header>` and `<footer>` elements in the generated HTML document or not. Default is `false`, which means that only the `<body>` part of the HTML document is generated.
All options/settings are passed via the `config` parameter.

=== Macro definition

The user can define a macro by calling `renderer.DefineMacro()` and passing return value to conversion functions.
The user can define a macro by calling `renderer.WithMacroTemplate()` and passing return value to conversion functions.

`renderer.DefineMacro()` defines a macro by the given name and associates the given template. The template is an implementation of `renderer.MacroTemplate` interface (ex. `text.Template`)
`renderer.WithMacroTemplate()` defines a macro by the given name and associates the given template. The template is an implementation of `renderer.MacroTemplate` interface (ex. `text.Template`)

Libasciidoc calls `Execute()` method and passes `types.UserMacro` object to template when rendering.

Expand All @@ -85,7 +85,7 @@ var tmpl = template.Must(t.Parse(tmplStr))

output := &strings.Builder{}
content := strings.NewReader(`example::hello world[suffix=!!!!!]`)
libasciidoc.ConvertToHTML(context.Background(), content, output, renderer.DefineMacro(tmpl.Name(), tmpl))
libasciidoc.ConvertToHTML(context.Background(), content, output, renderer.WithMacroTemplate(tmpl.Name(), tmpl))
```

== How to contribute
Expand Down
40 changes: 31 additions & 9 deletions cmd/libasciidoc/root_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"path/filepath"

"github.com/bytesparadise/libasciidoc"
"github.com/bytesparadise/libasciidoc/pkg/configuration"
logsupport "github.com/bytesparadise/libasciidoc/pkg/log"
"github.com/bytesparadise/libasciidoc/pkg/renderer"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -23,6 +23,7 @@ func NewRootCmd() *cobra.Command {
var outputName string
var logLevel string
var css string
var attributes []string

rootCmd := &cobra.Command{
Use: "libasciidoc [flags] FILE",
Expand All @@ -43,13 +44,19 @@ func NewRootCmd() *cobra.Command {
if len(args) == 0 {
return helpCommand.RunE(cmd, args)
}
for _, source := range args {
out, close := getOut(cmd, source, outputName)
attrs := parseAttributes(attributes)
for _, sourcePath := range args {
out, close := getOut(cmd, sourcePath, outputName)
if out != nil {
defer close()
path, _ := filepath.Abs(source)
path, _ := filepath.Abs(sourcePath)
log.Debugf("Starting to process file %v", path)
_, err := libasciidoc.ConvertFileToHTML(source, out, renderer.IncludeHeaderFooter(!noHeaderFooter), renderer.IncludeCSS(css))
config := configuration.NewConfiguration(
configuration.WithFilename(sourcePath),
configuration.WithAttributes(attrs),
configuration.WithCSS(css),
configuration.WithHeaderFooter(!noHeaderFooter))
_, err := libasciidoc.ConvertFileToHTML(out, config)
if err != nil {
return err
}
Expand All @@ -63,6 +70,7 @@ func NewRootCmd() *cobra.Command {
flags.StringVarP(&outputName, "out-file", "o", "", "output file (default: based on path of input file); use - to output to STDOUT")
flags.StringVar(&logLevel, "log", "warning", "log level to set [debug|info|warning|error|fatal|panic]")
flags.StringVar(&css, "css", "", "the path to the CSS file to link to the document")
flags.StringArrayVarP(&attributes, "attribute", "a", []string{}, "a document attribute to set in the form of name, name!, or name=value pair")
return rootCmd
}

Expand All @@ -78,7 +86,7 @@ func newCloseFileFunc(c io.Closer) closeFunc {
}
}

func getOut(cmd *cobra.Command, source, outputName string) (io.Writer, closeFunc) {
func getOut(cmd *cobra.Command, sourcePath, outputName string) (io.Writer, closeFunc) {
if outputName == "-" {
// outfile is STDOUT
return cmd.OutOrStdout(), defaultCloseFunc()
Expand All @@ -89,9 +97,9 @@ func getOut(cmd *cobra.Command, source, outputName string) (io.Writer, closeFunc
log.Warnf("Cannot create output file - %v, skipping", outputName)
}
return outfile, newCloseFileFunc(outfile)
} else if source != "" {
// outfile is based on source
path, _ := filepath.Abs(source)
} else if sourcePath != "" {
// outfile is based on sourcePath
path, _ := filepath.Abs(sourcePath)
outname := strings.TrimSuffix(path, filepath.Ext(path)) + ".html"
outfile, err := os.Create(outname)
if err != nil {
Expand All @@ -102,3 +110,17 @@ func getOut(cmd *cobra.Command, source, outputName string) (io.Writer, closeFunc
}
return cmd.OutOrStdout(), defaultCloseFunc()
}

// converts the `name`, `!name` and `name=value` into a map
func parseAttributes(attributes []string) map[string]string {
result := make(map[string]string, len(attributes))
for _, attr := range attributes {
data := strings.Split(attr, "=")
if len(data) > 1 {
result[data[0]] = data[1]
} else {
result[data[0]] = ""
}
}
return result
}
34 changes: 33 additions & 1 deletion cmd/libasciidoc/root_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
)

var _ = Describe("root cmd", func() {
RegisterFailHandler(Fail)

It("render with STDOUT output", func() {
// given
Expand Down Expand Up @@ -68,6 +67,38 @@ var _ = Describe("root cmd", func() {
Expect(buf.String()).ToNot(ContainSubstring(`<div id="footer">`))
})

It("render with attribute set", func() {
// given
root := main.NewRootCmd()
buf := new(bytes.Buffer)
root.SetOutput(buf)
root.SetArgs([]string{"-s", "-o", "-", "-afoo1=bar1", "-afoo2=bar2", "test/doc_with_attributes.adoc"})
// when
err := root.Execute()
// then
Expect(err).ToNot(HaveOccurred())
Expect(buf.String()).ToNot(BeEmpty())
Expect(buf.String()).To(Equal(`<div class="paragraph">
<p>bar1 and bar2</p>
</div>`))
})

It("render with attribute reset", func() {
// given
root := main.NewRootCmd()
buf := new(bytes.Buffer)
root.SetOutput(buf)
root.SetArgs([]string{"-s", "-o", "-", "-afoo1=bar1", "-a!foo2", "test/doc_with_attributes.adoc"})
// when
err := root.Execute()
// then
Expect(err).ToNot(HaveOccurred())
Expect(buf.String()).ToNot(BeEmpty())
Expect(buf.String()).To(Equal(`<div class="paragraph">
<p>bar1 and {foo2}</p>
</div>`))
})

It("render multiple files", func() {
// given
root := main.NewRootCmd()
Expand Down Expand Up @@ -103,4 +134,5 @@ var _ = Describe("root cmd", func() {
// then
Expect(err).ToNot(HaveOccurred())
})

})
5 changes: 5 additions & 0 deletions cmd/libasciidoc/test/doc_with_attributes.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:foo1: FOO1

:!foo1:

{foo1} and {foo2}
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.7.1 h1:G1i02OhUbRi2nJxcNkwJaY/J1gHXj9tt72qN6ZouLFQ=
github.com/alecthomas/chroma v0.7.1/go.mod h1:gHw09mkX1Qp80JlYbmN9L3+4R5o6DJJ3GRShh+AICNc=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
Expand All @@ -24,6 +27,7 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mna/pigeon v1.0.1-0.20190909211542-7ee56e19b15c h1:QRaadf9Fu8xAfNDS8PvaM0VmY2FnYHlddtnIExKj68k=
Expand All @@ -35,8 +39,6 @@ github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -61,8 +63,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83 h1:mgAKeshyNqWKdENOnQsg+8dRTwZFIwFaO3HNl52sweA=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
Expand Down Expand Up @@ -91,8 +91,6 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911225940-c7d52e45e2f2 h1:4m1a+ssoIi4N/776T3Dc79eRjDc5sEhMQXFAKoO3TM8=
golang.org/x/tools v0.0.0-20190911225940-c7d52e45e2f2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191220234730-f13409bbebaf h1:K7C8vSrr0PeD/cgNkkjpByDFJqzjr2YDmm3VPRjGfJM=
golang.org/x/tools v0.0.0-20191220234730-f13409bbebaf/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
Expand Down
23 changes: 12 additions & 11 deletions libasciidoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import (
"os"
"time"

"github.com/bytesparadise/libasciidoc/pkg/configuration"
"github.com/bytesparadise/libasciidoc/pkg/parser"
"github.com/bytesparadise/libasciidoc/pkg/renderer"
htmlrenderer "github.com/bytesparadise/libasciidoc/pkg/renderer/html5"
"github.com/bytesparadise/libasciidoc/pkg/types"

"github.com/pkg/errors"

log "github.com/sirupsen/logrus"
)

Expand All @@ -28,35 +29,35 @@ var (
// ConvertFileToHTML converts the content of the given filename into an HTML document.
// The conversion result is written in the given writer `output`, whereas the document metadata (title, etc.) (or an error if a problem occurred) is returned
// as the result of the function call.
func ConvertFileToHTML(filename string, output io.Writer, options ...renderer.Option) (types.Metadata, error) {
file, err := os.Open(filename)
func ConvertFileToHTML(output io.Writer, config configuration.Configuration) (types.Metadata, error) {
file, err := os.Open(config.Filename)
if err != nil {
return types.Metadata{}, errors.Wrapf(err, "error opening %s", filename)
return types.Metadata{}, errors.Wrapf(err, "error opening %s", config.Filename)
}
defer file.Close()
// use the file mtime as the `last updated` value
stat, err := os.Stat(filename)
stat, err := os.Stat(config.Filename)
if err != nil {
return types.Metadata{}, errors.Wrapf(err, "error opening %s", filename)
return types.Metadata{}, errors.Wrapf(err, "error opening %s", config.Filename)
}
options = append(options, renderer.LastUpdated(stat.ModTime()))
return ConvertToHTML(filename, file, output, options...)
config.LastUpdated = stat.ModTime()
return ConvertToHTML(file, output, config)
}

// ConvertToHTML converts the content of the given reader `r` into a full HTML document, written in the given writer `output`.
// Returns an error if a problem occurred
func ConvertToHTML(filename string, r io.Reader, output io.Writer, options ...renderer.Option) (types.Metadata, error) {
func ConvertToHTML(r io.Reader, output io.Writer, config configuration.Configuration) (types.Metadata, error) {
start := time.Now()
defer func() {
duration := time.Since(start)
log.Debugf("rendered the HTML output in %v", duration)
}()
log.Debugf("parsing the asciidoc source...")
doc, err := parser.ParseDocument(filename, r) //, parser.Debug(true))
doc, err := parser.ParseDocument(r, config) //, parser.Debug(true))
if err != nil {
return types.Metadata{}, err
}
rendererCtx := renderer.NewContext(doc, options...)
rendererCtx := renderer.NewContext(doc, config)
// insert tables of contents, preamble and process file inclusions
err = renderer.Prerender(rendererCtx)
if err != nil {
Expand Down
14 changes: 7 additions & 7 deletions libasciidoc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"os"
"time"

"github.com/bytesparadise/libasciidoc/pkg/renderer"
"github.com/bytesparadise/libasciidoc/pkg/configuration"
"github.com/bytesparadise/libasciidoc/pkg/types"
. "github.com/bytesparadise/libasciidoc/testsupport"

Expand Down Expand Up @@ -113,7 +113,7 @@ a paragraph`
Expect(RenderHTML5Title(source)).To(Equal(expectedTitle))
Expect(DocumentMetadata(source, lastUpdated)).To(Equal(types.Metadata{
Title: "a document title",
LastUpdated: lastUpdated.Format(renderer.LastUpdatedFormat),
LastUpdated: lastUpdated.Format(configuration.LastUpdatedFormat),
TableOfContents: types.TableOfContents{
Sections: []types.ToCSection{
{
Expand Down Expand Up @@ -175,7 +175,7 @@ a paragraph with _italic content_`
Expect(RenderHTML5Title(source)).To(Equal(expectedTitle))
Expect(DocumentMetadata(source, lastUpdated)).To(Equal(types.Metadata{
Title: "a document title",
LastUpdated: lastUpdated.Format(renderer.LastUpdatedFormat),
LastUpdated: lastUpdated.Format(configuration.LastUpdatedFormat),
TableOfContents: types.TableOfContents{
Sections: []types.ToCSection{
{
Expand Down Expand Up @@ -215,10 +215,10 @@ a paragraph with _italic content_`
</div>
</div>
</div>`
Expect(RenderHTML5Body(source, WithFilename("test.adoc"), renderer.LastUpdated(lastUpdated))).To(Equal(expected))
Expect(RenderHTML5Body(source, configuration.WithFilename("test.adoc"), configuration.WithLastUpdated(lastUpdated))).To(Equal(expected))
Expect(DocumentMetadata(source, lastUpdated)).To(Equal(types.Metadata{
Title: "",
LastUpdated: lastUpdated.Format(renderer.LastUpdatedFormat),
LastUpdated: lastUpdated.Format(configuration.LastUpdatedFormat),
TableOfContents: types.TableOfContents{
Sections: []types.ToCSection{
{
Expand All @@ -245,7 +245,7 @@ a paragraph with _italic content_`
</div>
</div>
</div>`
Expect(RenderHTML5Body(source, WithFilename("tmp/foo.adoc"))).To(Equal(expectedContent))
Expect(RenderHTML5Body(source, configuration.WithFilename("tmp/foo.adoc"))).To(Equal(expectedContent))
})
})

Expand Down Expand Up @@ -281,7 +281,7 @@ Last updated {{.LastUpdated}}
filename := "test/includes/chapter-a.adoc"
stat, err := os.Stat(filename)
Expect(err).NotTo(HaveOccurred())
Expect(RenderHTML5Document(filename, renderer.IncludeCSS("path/to/style.css"))).To(MatchHTML5Template(expectedContent, stat.ModTime()))
Expect(RenderHTML5Document(filename, configuration.WithCSS("path/to/style.css"), configuration.WithHeaderFooter(true))).To(MatchHTML5Template(expectedContent, stat.ModTime()))
})

})
Expand Down
Loading