diff options
| author | Shulhan <ms@kilabit.info> | 2022-07-31 23:54:29 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2022-08-01 00:24:19 +0700 |
| commit | 8d347992ab2753dc878f56439883ec868e8d038d (patch) | |
| tree | 19ad6b40ee3898110742850b4755f6b7c0aefb67 | |
| parent | cd24f44c54b0a4a22fa9d4413fffab718df9052a (diff) | |
| download | ciigo-8d347992ab2753dc878f56439883ec868e8d038d.tar.xz | |
all: export internal htmlGenerator as Converter
The purpose of Converter is to provide a single, reusable converter
for AsciiDoc file or content.
| -rw-r--r-- | ciigo.go | 23 | ||||
| -rw-r--r-- | converter.go | 134 | ||||
| -rw-r--r-- | htmlgenerator.go | 142 | ||||
| -rw-r--r-- | server.go | 29 | ||||
| -rw-r--r-- | watcher.go | 32 | ||||
| -rw-r--r-- | watcher_test.go | 20 |
6 files changed, 195 insertions, 185 deletions
@@ -41,7 +41,7 @@ func Convert(opts *ConvertOptions) (err error) { var ( logp = "Convert" - htmlg *htmlGenerator + converter *Converter fileMarkups map[string]*fileMarkup ) @@ -53,7 +53,7 @@ func Convert(opts *ConvertOptions) (err error) { return fmt.Errorf("%s: %w", logp, err) } - htmlg, err = newHTMLGenerator(nil, opts.HtmlTemplate, true) + converter, err = NewConverter(opts.HtmlTemplate) if err != nil { return fmt.Errorf("%s: %w", logp, err) } @@ -63,7 +63,7 @@ func Convert(opts *ConvertOptions) (err error) { return fmt.Errorf("%s: %w", logp, err) } - htmlg.convertFileMarkups(fileMarkups, false) + converter.convertFileMarkups(fileMarkups, false) return nil } @@ -82,7 +82,7 @@ func GoEmbed(opts *EmbedOptions) (err error) { var ( logp = "GoEmbed" - htmlg *htmlGenerator + converter *Converter fileMarkups map[string]*fileMarkup mfs *memfs.MemFS convertForce bool @@ -96,7 +96,7 @@ func GoEmbed(opts *EmbedOptions) (err error) { return fmt.Errorf("%s: %w", logp, err) } - htmlg, err = newHTMLGenerator(nil, opts.HtmlTemplate, true) + converter, err = NewConverter(opts.HtmlTemplate) if err != nil { return fmt.Errorf("%s: %w", logp, err) } @@ -110,7 +110,7 @@ func GoEmbed(opts *EmbedOptions) (err error) { convertForce = true } - htmlg.convertFileMarkups(fileMarkups, convertForce) + converter.convertFileMarkups(fileMarkups, convertForce) memfsOpts := &memfs.Options{ Root: opts.Root, @@ -175,9 +175,10 @@ func Serve(opts *ServeOptions) (err error) { // default HTML template. func Watch(opts *ConvertOptions) (err error) { var ( - logp = "Watch" - htmlg *htmlGenerator - w *watcher + logp = "Watch" + + converter *Converter + w *watcher ) if opts == nil { @@ -188,12 +189,12 @@ func Watch(opts *ConvertOptions) (err error) { return fmt.Errorf("%s: %w", logp, err) } - htmlg, err = newHTMLGenerator(nil, opts.HtmlTemplate, true) + converter, err = NewConverter(opts.HtmlTemplate) if err != nil { return fmt.Errorf("%s: %w", logp, err) } - w, err = newWatcher(htmlg, opts) + w, err = newWatcher(converter, opts) if err != nil { return fmt.Errorf("%s: %w", logp, err) } diff --git a/converter.go b/converter.go new file mode 100644 index 0000000..58afad3 --- /dev/null +++ b/converter.go @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: 2019 Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later + +package ciigo + +import ( + "fmt" + "html/template" + "os" + "path/filepath" + + "git.sr.ht/~shulhan/asciidoctor-go" +) + +// Converter a single, reusable AsciiDoc converter. +type Converter struct { + tmpl *template.Template + tmplSearch *template.Template + htmlTemplate string +} + +// NewConverter create and initialize Converter with HTML template. +// If htmlTemplate is empty, it will use the internal, predefined template. +func NewConverter(htmlTemplate string) (converter *Converter, err error) { + var ( + logp = "NewConverter" + + tmplContent string + bhtml []byte + ) + + converter = &Converter{} + + converter.tmpl = template.New("") + + if len(htmlTemplate) == 0 { + tmplContent = templateIndexHTML + } else { + converter.htmlTemplate = filepath.Clean(htmlTemplate) + + bhtml, err = os.ReadFile(converter.htmlTemplate) + if err != nil { + return nil, fmt.Errorf("%s: %s: %w", logp, converter.htmlTemplate, err) + } + + tmplContent = string(bhtml) + } + + converter.tmpl, err = converter.tmpl.Parse(tmplContent) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) + } + + converter.tmplSearch = template.New("search") + converter.tmplSearch, err = converter.tmplSearch.Parse(templateSearch) + if err != nil { + return nil, fmt.Errorf("%s: %s: %w", logp, templateSearch, err) + } + + return converter, nil +} + +// convert the markup into HTML. +func (converter *Converter) convert(fmarkup *fileMarkup) (err error) { + doc, err := asciidoctor.Open(fmarkup.path) + if err != nil { + return err + } + + fmarkup.fhtml.rawBody.Reset() + err = doc.ToHTMLBody(&fmarkup.fhtml.rawBody) + if err != nil { + return err + } + + fmarkup.fhtml.unpackAdocMetadata(doc) + + return converter.write(fmarkup.fhtml) +} + +// convertFileMarkups convert markup files into HTML. +func (converter *Converter) convertFileMarkups(fileMarkups map[string]*fileMarkup, isForce bool) { + logp := "convertFileMarkups" + for _, fmarkup := range fileMarkups { + if !fmarkup.isNewerThanHtml() { + if !isForce { + continue + } + } + + err := converter.convert(fmarkup) + if err != nil { + fmt.Printf("%s: %s\n", logp, err) + } else { + fmt.Printf("%s: converting %s\n", logp, fmarkup.path) + } + } +} + +func (converter *Converter) htmlTemplateReload() (err error) { + converter.tmpl, err = template.ParseFiles(converter.htmlTemplate) + if err != nil { + return err + } + return nil +} + +func (converter *Converter) htmlTemplateUseInternal() (err error) { + converter.tmpl, err = converter.tmpl.Parse(templateIndexHTML) + if err != nil { + return err + } + return nil +} + +// write the HTML file. +func (converter *Converter) write(fhtml *fileHtml) (err error) { + f, err := os.Create(fhtml.path) + if err != nil { + return err + } + + err = converter.tmpl.Execute(f, fhtml) + if err != nil { + return err + } + + err = f.Close() + if err != nil { + return err + } + + return nil +} diff --git a/htmlgenerator.go b/htmlgenerator.go deleted file mode 100644 index c2e0b01..0000000 --- a/htmlgenerator.go +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-FileCopyrightText: 2019 Shulhan <ms@kilabit.info> -// SPDX-License-Identifier: GPL-3.0-or-later - -package ciigo - -import ( - "fmt" - "html/template" - "os" - "path/filepath" - - "git.sr.ht/~shulhan/asciidoctor-go" - "github.com/shuLhan/share/lib/memfs" -) - -// htmlGenerator provide a template to write full HTML file. -type htmlGenerator struct { - tmpl *template.Template - tmplSearch *template.Template - htmlTemplate string -} - -func newHTMLGenerator(mfs *memfs.MemFS, htmlTemplate string, devel bool) ( - htmlg *htmlGenerator, err error, -) { - var logp = "newHTMLGenerator" - - htmlg = &htmlGenerator{} - - htmlg.tmpl = template.New("") - - if len(htmlTemplate) == 0 { - htmlg.tmpl, err = htmlg.tmpl.Parse(templateIndexHTML) - if err != nil { - return nil, fmt.Errorf("%s: %s: %w", logp, templateIndexHTML, err) - } - } else if mfs == nil || devel { - htmlg.htmlTemplate = filepath.Clean(htmlTemplate) - - bhtml, err := os.ReadFile(htmlg.htmlTemplate) - if err != nil { - return nil, fmt.Errorf("%s: %s: %w", logp, htmlg.htmlTemplate, err) - } - - htmlg.tmpl, err = htmlg.tmpl.Parse(string(bhtml)) - if err != nil { - return nil, fmt.Errorf("%s: Parse: %s", logp, err) - } - } else { - // Load HTML template from memory file system. - tmplNode, err := mfs.Get(internalTemplatePath) - if err != nil { - return nil, fmt.Errorf("%s: %s: %w", logp, internalTemplatePath, err) - } - - htmlg.tmpl, err = htmlg.tmpl.Parse(string(tmplNode.Content)) - if err != nil { - return nil, fmt.Errorf("%s: %s", logp, err) - } - } - - htmlg.tmplSearch = template.New("search") - htmlg.tmplSearch, err = htmlg.tmplSearch.Parse(templateSearch) - if err != nil { - return nil, fmt.Errorf("%s: %s: %w", logp, templateSearch, err) - } - - return htmlg, nil -} - -// convert the markup into HTML. -func (htmlg *htmlGenerator) convert(fmarkup *fileMarkup) (err error) { - doc, err := asciidoctor.Open(fmarkup.path) - if err != nil { - return err - } - - fmarkup.fhtml.rawBody.Reset() - err = doc.ToHTMLBody(&fmarkup.fhtml.rawBody) - if err != nil { - return err - } - - fmarkup.fhtml.unpackAdocMetadata(doc) - - return htmlg.write(fmarkup.fhtml) -} - -// convertFileMarkups convert markup files into HTML. -func (htmlg *htmlGenerator) convertFileMarkups(fileMarkups map[string]*fileMarkup, isForce bool) { - logp := "convertFileMarkups" - for _, fmarkup := range fileMarkups { - if !fmarkup.isNewerThanHtml() { - if !isForce { - continue - } - } - - err := htmlg.convert(fmarkup) - if err != nil { - fmt.Printf("%s: %s\n", logp, err) - } else { - fmt.Printf("%s: converting %s\n", logp, fmarkup.path) - } - } -} - -func (htmlg *htmlGenerator) htmlTemplateReload() (err error) { - htmlg.tmpl, err = template.ParseFiles(htmlg.htmlTemplate) - if err != nil { - return err - } - return nil -} - -func (htmlg *htmlGenerator) htmlTemplateUseInternal() (err error) { - htmlg.tmpl, err = htmlg.tmpl.Parse(templateIndexHTML) - if err != nil { - return err - } - return nil -} - -// write the HTML file. -func (htmlg *htmlGenerator) write(fhtml *fileHtml) (err error) { - f, err := os.Create(fhtml.path) - if err != nil { - return err - } - - err = htmlg.tmpl.Execute(f, fhtml) - if err != nil { - return err - } - - err = f.Close() - if err != nil { - return err - } - - return nil -} @@ -15,10 +15,10 @@ import ( // server contains the HTTP server that serve the generated HTML files. type server struct { - http *libhttp.Server - htmlg *htmlGenerator - watcher *watcher - opts ServeOptions + http *libhttp.Server + converter *Converter + watcher *watcher + opts ServeOptions } // newServer create an HTTP server to serve HTML files in directory "root". @@ -29,6 +29,8 @@ type server struct { func newServer(opts *ServeOptions) (srv *server, err error) { var ( logp = "newServer" + + tmplNode *memfs.Node ) if opts.Mfs == nil { @@ -70,18 +72,27 @@ func newServer(opts *ServeOptions) (srv *server, err error) { return nil, fmt.Errorf("%s: %w", logp, err) } - srv.htmlg, err = newHTMLGenerator(opts.Mfs, opts.HtmlTemplate, opts.IsDevelopment) + srv.converter, err = NewConverter(opts.HtmlTemplate) if err != nil { return nil, fmt.Errorf("%s: %w", logp, err) } + // Optionally, load HTML template from memory file system. + tmplNode, _ = opts.Mfs.Get(internalTemplatePath) + if tmplNode != nil { + srv.converter.tmpl, err = srv.converter.tmpl.Parse(string(tmplNode.Content)) + if err != nil { + return nil, fmt.Errorf("%s: %s", logp, err) + } + } + if opts.IsDevelopment { - srv.watcher, err = newWatcher(srv.htmlg, &opts.ConvertOptions) + srv.watcher, err = newWatcher(srv.converter, &opts.ConvertOptions) if err != nil { return nil, fmt.Errorf("%s: %w", logp, err) } - srv.htmlg.convertFileMarkups(srv.watcher.fileMarkups, false) + srv.converter.convertFileMarkups(srv.watcher.fileMarkups, false) } return srv, nil @@ -116,7 +127,7 @@ func (srv *server) onSearch(epr *libhttp.EndpointRequest) (resBody []byte, err e q := epr.HttpRequest.Form.Get("q") results := srv.http.Options.Memfs.Search(strings.Fields(q), 0) - err = srv.htmlg.tmplSearch.Execute(&bufSearch, results) + err = srv.converter.tmplSearch.Execute(&bufSearch, results) if err != nil { return nil, fmt.Errorf("%s: %w", logp, err) } @@ -125,7 +136,7 @@ func (srv *server) onSearch(epr *libhttp.EndpointRequest) (resBody []byte, err e Body: template.HTML(bufSearch.String()), //nolint: gosec } - err = srv.htmlg.tmpl.Execute(&buf, fhtml) + err = srv.converter.tmpl.Execute(&buf, fhtml) if err != nil { return nil, fmt.Errorf("%s: %w", logp, err) } @@ -20,7 +20,7 @@ type watcher struct { changes *clise.Clise watchDir *memfs.DirWatcher watchTemplate *memfs.Watcher - htmlg *htmlGenerator + converter *Converter // fileMarkups contains all markup files found inside "dir". // Its used to convert all markup files when the template file @@ -33,25 +33,25 @@ type watcher struct { // newWatcher create a watcher that monitor every files changes in directory // "dir" for new, modified, and deleted markup files and HTML template file. // -// The watcher depends on htmlGenerator to convert the markup to HTML using -// the HTML template in htmlGenerator. +// The watcher depends on Converter to convert the markup to HTML using +// the HTML template in Converter. // // watcher // | -// +-- watchFileMarkup --> UPDATE --> htmlGenerator.convert() +// +-- watchFileMarkup --> UPDATE --> Converter.convert() // | -// +-- watchHtmlTemplate +--> DELETE --> htmlGenerator.htmlTemplateUseInternal() +// +-- watchHtmlTemplate +--> DELETE --> Converter.htmlTemplateUseInternal() // | -// +--> UPDATE --> htmlGenerated.htmlTemplateReload() -func newWatcher(htmlg *htmlGenerator, convertOpts *ConvertOptions) (w *watcher, err error) { +// +--> UPDATE --> Converter.htmlTemplateReload() +func newWatcher(converter *Converter, convertOpts *ConvertOptions) (w *watcher, err error) { var ( logp = "newWatcher" ) w = &watcher{ - dir: convertOpts.Root, - htmlg: htmlg, - changes: clise.New(1), + dir: convertOpts.Root, + converter: converter, + changes: clise.New(1), } w.watchDir = &memfs.DirWatcher{ Options: memfs.Options{ @@ -89,8 +89,8 @@ func (w *watcher) start() (err error) { go w.watchFileMarkup() - if len(w.htmlg.htmlTemplate) > 0 { - w.watchTemplate, err = memfs.NewWatcher(w.htmlg.htmlTemplate, 0) + if len(w.converter.htmlTemplate) > 0 { + w.watchTemplate, err = memfs.NewWatcher(w.converter.htmlTemplate, 0) if err != nil { return fmt.Errorf("start: %w", err) } @@ -156,7 +156,7 @@ func (w *watcher) watchFileMarkup() { } } - err = w.htmlg.convert(fmarkup) + err = w.converter.convert(fmarkup) if err != nil { log.Printf("%s: %s\n", logp, err) } @@ -179,11 +179,11 @@ func (w *watcher) watchHtmlTemplate() { if ns.State == memfs.FileStateDeleted { log.Printf("%s: HTML template file %q has been deleted\n", logp, ns.Node.SysPath) - err = w.htmlg.htmlTemplateUseInternal() + err = w.converter.htmlTemplateUseInternal() } else { fmt.Printf("%s: recompiling HTML template %q ...\n", logp, ns.Node.SysPath) - err = w.htmlg.htmlTemplateReload() + err = w.converter.htmlTemplateReload() } if err != nil { log.Printf("%s: %s", logp, err) @@ -191,6 +191,6 @@ func (w *watcher) watchHtmlTemplate() { } fmt.Printf("%s: regenerate all markup files ...\n", logp) - w.htmlg.convertFileMarkups(w.fileMarkups, true) + w.converter.convertFileMarkups(w.fileMarkups, true) } } diff --git a/watcher_test.go b/watcher_test.go index 3ce17ee..5cc2079 100644 --- a/watcher_test.go +++ b/watcher_test.go @@ -20,9 +20,17 @@ var ( ) func TestWatcher(t *testing.T) { - testDir := "testdata/watcher" + var ( + testDir = "testdata/watcher" + convertOpts = ConvertOptions{ + Root: testDir, + } + + converter *Converter + err error + ) - err := os.RemoveAll(testDir) + err = os.RemoveAll(testDir) if err != nil { t.Logf(err.Error()) } @@ -36,20 +44,17 @@ func TestWatcher(t *testing.T) { os.RemoveAll(testDir) }) - htmlg, err := newHTMLGenerator(nil, "testdata/html.tmpl", true) + converter, err = NewConverter("testdata/html.tmpl") if err != nil { t.Fatal(err) } - convertOpts := ConvertOptions{ - Root: testDir, - } err = convertOpts.init() if err != nil { t.Fatal(err) } - testWatcher, err = newWatcher(htmlg, &convertOpts) + testWatcher, err = newWatcher(converter, &convertOpts) if err != nil { t.Fatal(err) } @@ -68,6 +73,7 @@ func testCreate(t *testing.T) { var ( err error ) + testFileAdoc = filepath.Join(testWatcher.dir, "index.adoc") testAdocFile, err = os.Create(testFileAdoc) if err != nil { |
