summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ciigo.go265
-rw-r--r--convert_options.go2
-rw-r--r--server.go81
-rw-r--r--watcher.go2
-rw-r--r--watcher_test.go2
5 files changed, 180 insertions, 172 deletions
diff --git a/ciigo.go b/ciigo.go
index cdfccff..01e11cf 100644
--- a/ciigo.go
+++ b/ciigo.go
@@ -17,6 +17,7 @@ import (
"regexp"
"strings"
+ libhttp "git.sr.ht/~shulhan/pakakeh.go/lib/http"
"git.sr.ht/~shulhan/pakakeh.go/lib/memfs"
)
@@ -36,39 +37,27 @@ var defExcludes = []string{
`^\..*`,
}
+// Ciigo provides customizable and reusable instance of ciigo for embedding,
+// converting, and/or serving HTTP server.
+// This type is introduced so one can add HTTP handler or endpoint along
+// with serving the files.
+type Ciigo struct {
+ HTTPServer *libhttp.Server
+ converter *Converter
+ watcher *watcher
+ serveOpts ServeOptions
+}
+
// Convert all markup files inside directory "dir" recursively into HTML
// files using ConvertOptions HTMLTemplate file as base template.
// If HTMLTemplate is empty it will default to use embedded HTML template.
// See template_index_html.go for template format.
func Convert(opts *ConvertOptions) (err error) {
- var (
- logp = `Convert`
-
- converter *Converter
- fileMarkups map[string]*FileMarkup
- )
-
if opts == nil {
opts = &ConvertOptions{}
}
- err = opts.init()
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- converter, err = NewConverter(opts.HTMLTemplate)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- fileMarkups, err = listFileMarkups(opts.Root, opts.excRE)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- converter.convertFileMarkups(fileMarkups, false)
-
- return nil
+ var ciigo = &Ciigo{}
+ return ciigo.Convert(*opts)
}
// GoEmbed generate a static Go file that embed all files inside Root except
@@ -82,89 +71,25 @@ func Convert(opts *ConvertOptions) (err error) {
// template.
// See template_index_html.go for template format.
func GoEmbed(opts *EmbedOptions) (err error) {
- var (
- logp = `GoEmbed`
-
- converter *Converter
- fileMarkups map[string]*FileMarkup
- mfs *memfs.MemFS
- mfsOpts *memfs.Options
- convertForce bool
- )
-
if opts == nil {
opts = &EmbedOptions{}
}
- err = opts.init()
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- converter, err = NewConverter(opts.HTMLTemplate)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- fileMarkups, err = listFileMarkups(opts.Root, opts.excRE)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- convertForce = isHTMLTemplateNewer(opts)
-
- converter.convertFileMarkups(fileMarkups, convertForce)
-
- mfsOpts = &memfs.Options{
- Root: opts.Root,
- Excludes: defExcludes,
- Embed: opts.EmbedOptions,
- }
-
- mfs, err = memfs.New(mfsOpts)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- if len(opts.HTMLTemplate) > 0 {
- _, err = mfs.AddFile(internalTemplatePath, opts.HTMLTemplate)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
- }
-
- err = mfs.GoEmbed()
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- return nil
+ var ciigo = &Ciigo{}
+ return ciigo.GoEmbed(*opts)
}
-// Serve the content at directory "dir" using HTTP server at specific
-// "address".
+// Serve the content under directory "[ServeOptions].ConvertOptions.Root"
+// using HTTP server at specific "[ServeOptions].Address".
func Serve(opts *ServeOptions) (err error) {
- var (
- logp = `Serve`
- srv *server
- )
-
if opts == nil {
opts = &ServeOptions{}
}
- err = opts.init()
+ var ciigo = &Ciigo{}
+ err = ciigo.InitHTTPServer(*opts)
if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
+ return err
}
-
- srv, err = newServer(opts)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
- err = srv.start()
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
- return nil
+ return ciigo.Serve()
}
// Watch any changes on markup files on directory Root recursively and
@@ -176,42 +101,16 @@ func Serve(opts *ServeOptions) (err error) {
// If the HTML template file deleted, it will replace them with internal,
// default HTML template.
func Watch(opts *ConvertOptions) (err error) {
- var (
- logp = `Watch`
-
- converter *Converter
- w *watcher
- )
-
if opts == nil {
opts = &ConvertOptions{}
}
- err = opts.init()
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- converter, err = NewConverter(opts.HTMLTemplate)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- w, err = newWatcher(converter, opts)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- err = w.start()
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- return nil
+ var ciigo = &Ciigo{}
+ return ciigo.Watch(*opts)
}
// isHTMLTemplateNewer will return true if HTMLTemplate is not defined or
// newer than embedded GoFileName.
-func isHTMLTemplateNewer(opts *EmbedOptions) bool {
+func isHTMLTemplateNewer(opts EmbedOptions) bool {
var (
logp = `isHTMLTemplateNewer`
@@ -348,3 +247,117 @@ func isExcluded(path string, excs []*regexp.Regexp) bool {
}
return false
}
+
+// Convert all markup files inside directory [ConvertOptions.Root]
+// recursively into HTML files using [ConvertOptions.HTMLTemplate] file as
+// base template.
+// If HTMLTemplate is empty it use the default embedded HTML template.
+// See template_index_html.go for template format.
+func (ciigo *Ciigo) Convert(opts ConvertOptions) (err error) {
+ var logp = `Convert`
+
+ err = opts.init()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ ciigo.serveOpts.ConvertOptions = opts
+
+ ciigo.converter, err = NewConverter(opts.HTMLTemplate)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ var fileMarkups map[string]*FileMarkup
+
+ fileMarkups, err = listFileMarkups(opts.Root, opts.excRE)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ ciigo.converter.convertFileMarkups(fileMarkups, false)
+
+ return nil
+}
+
+// GoEmbed embed the file system (directories and files) inside the
+// [ConvertOptions.Root] into a Go code.
+// One can exclude files by writing regular expression in
+// [ConvertOptions.Exclude].
+func (ciigo *Ciigo) GoEmbed(embedOpts EmbedOptions) (err error) {
+ var logp = `GoEmbed`
+
+ err = embedOpts.init()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ ciigo.converter, err = NewConverter(embedOpts.HTMLTemplate)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ var fileMarkups map[string]*FileMarkup
+
+ fileMarkups, err = listFileMarkups(embedOpts.Root, embedOpts.excRE)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ var convertForce = isHTMLTemplateNewer(embedOpts)
+
+ ciigo.converter.convertFileMarkups(fileMarkups, convertForce)
+
+ var mfsOpts = &memfs.Options{
+ Root: embedOpts.Root,
+ Excludes: defExcludes,
+ Embed: embedOpts.EmbedOptions,
+ }
+
+ var mfs *memfs.MemFS
+
+ mfs, err = memfs.New(mfsOpts)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ if len(embedOpts.HTMLTemplate) > 0 {
+ _, err = mfs.AddFile(internalTemplatePath, embedOpts.HTMLTemplate)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+ }
+
+ err = mfs.GoEmbed()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+ return nil
+}
+
+// Watch start a watcher on [ConvertOptions.Root] directory that monitor any
+// changes to markup files and convert them to HTML files.
+func (ciigo *Ciigo) Watch(convertOpts ConvertOptions) (err error) {
+ var logp = `Watch`
+
+ err = convertOpts.init()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ ciigo.converter, err = NewConverter(convertOpts.HTMLTemplate)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ ciigo.watcher, err = newWatcher(ciigo.converter, convertOpts)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ err = ciigo.watcher.start()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+ return nil
+}
diff --git a/convert_options.go b/convert_options.go
index 13741ee..0423644 100644
--- a/convert_options.go
+++ b/convert_options.go
@@ -9,7 +9,7 @@ import (
)
const (
- // DefaultRoot define default Root value for GenerateOptions.
+ // DefaultRoot define default Root value for ConvertOptions.
DefaultRoot = `.`
)
diff --git a/server.go b/server.go
index 8ad76d1..3d10b5f 100644
--- a/server.go
+++ b/server.go
@@ -14,21 +14,19 @@ import (
"git.sr.ht/~shulhan/pakakeh.go/lib/memfs"
)
-// server contains the HTTP server that serve the generated HTML files.
-type server struct {
- http *libhttp.Server
- converter *Converter
- watcher *watcher
- opts ServeOptions
-}
-
-// newServer create an HTTP server to serve HTML files in directory "root".
+// InitHTTPServer create an HTTP server to serve HTML files in directory
+// defined in "[ConvertOptions].Root".
//
// The address parameter is optional, if not set its default to ":8080".
// The htmlTemplate parameter is optional, if not set its default to
// embedded HTML template.
-func newServer(opts *ServeOptions) (srv *server, err error) {
- var logp = `newServer`
+func (ciigo *Ciigo) InitHTTPServer(opts ServeOptions) (err error) {
+ var logp = `initServer`
+
+ err = opts.init()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
if opts.Mfs == nil {
opts.IsDevelopment = true
@@ -39,15 +37,13 @@ func newServer(opts *ServeOptions) (srv *server, err error) {
}
opts.Mfs, err = memfs.New(mfsopts)
if err != nil {
- return nil, fmt.Errorf(`%s: %w`, logp, err)
+ return fmt.Errorf(`%s: %w`, logp, err)
}
} else {
opts.Mfs.Opts.TryDirect = opts.IsDevelopment
}
- srv = &server{
- opts: *opts,
- }
+ ciigo.serveOpts = opts
var httpdOpts = libhttp.ServerOptions{
Memfs: opts.Mfs,
@@ -55,9 +51,9 @@ func newServer(opts *ServeOptions) (srv *server, err error) {
EnableIndexHTML: opts.EnableIndexHTML,
}
- srv.http, err = libhttp.NewServer(httpdOpts)
+ ciigo.HTTPServer, err = libhttp.NewServer(httpdOpts)
if err != nil {
- return nil, fmt.Errorf(`%s: %w`, logp, err)
+ return fmt.Errorf(`%s: %w`, logp, err)
}
var epInSearch = libhttp.Endpoint{
@@ -65,12 +61,12 @@ func newServer(opts *ServeOptions) (srv *server, err error) {
Path: `/_internal/search`,
RequestType: libhttp.RequestTypeQuery,
ResponseType: libhttp.ResponseTypeHTML,
- Call: srv.onSearch,
+ Call: ciigo.onSearch,
}
- err = srv.http.RegisterEndpoint(epInSearch)
+ err = ciigo.HTTPServer.RegisterEndpoint(epInSearch)
if err != nil {
- return nil, fmt.Errorf(`%s: %w`, logp, err)
+ return fmt.Errorf(`%s: %w`, logp, err)
}
var pathHTMLTemplate string
@@ -79,9 +75,9 @@ func newServer(opts *ServeOptions) (srv *server, err error) {
pathHTMLTemplate = opts.HTMLTemplate
}
- srv.converter, err = NewConverter(pathHTMLTemplate)
+ ciigo.converter, err = NewConverter(pathHTMLTemplate)
if err != nil {
- return nil, fmt.Errorf(`%s: %w`, logp, err)
+ return fmt.Errorf(`%s: %w`, logp, err)
}
if !opts.IsDevelopment {
@@ -89,54 +85,53 @@ func newServer(opts *ServeOptions) (srv *server, err error) {
tmplNode, _ = opts.Mfs.Get(internalTemplatePath)
if tmplNode != nil {
- srv.converter.tmpl, err = srv.converter.tmpl.Parse(string(tmplNode.Content))
+ ciigo.converter.tmpl, err = ciigo.converter.tmpl.Parse(string(tmplNode.Content))
if err != nil {
- return nil, fmt.Errorf(`%s: %s`, logp, err)
+ return fmt.Errorf(`%s: %s`, logp, err)
}
}
}
if opts.IsDevelopment {
- srv.watcher, err = newWatcher(srv.converter, &opts.ConvertOptions)
+ ciigo.watcher, err = newWatcher(ciigo.converter, opts.ConvertOptions)
if err != nil {
- return nil, fmt.Errorf(`%s: %w`, logp, err)
+ return fmt.Errorf(`%s: %w`, logp, err)
}
- srv.converter.convertFileMarkups(srv.watcher.fileMarkups, false)
+ ciigo.converter.convertFileMarkups(ciigo.watcher.fileMarkups, false)
}
- return srv, nil
+ return nil
}
-// start the web server.
-func (srv *server) start() (err error) {
- var (
- logp = `start`
- )
+// Serve start the HTTP web server.
+func (ciigo *Ciigo) Serve() (err error) {
+ var logp = `Serve`
- if srv.opts.IsDevelopment {
- err = srv.watcher.start()
+ if ciigo.serveOpts.IsDevelopment {
+ err = ciigo.watcher.start()
if err != nil {
return fmt.Errorf(`%s: %w`, logp, err)
}
}
log.Printf(`ciigo: starting HTTP server at http://%s for %q`,
- srv.http.Options.Address, srv.http.Options.Memfs.Opts.Root)
+ ciigo.HTTPServer.Options.Address,
+ ciigo.HTTPServer.Options.Memfs.Opts.Root)
- err = srv.http.Start()
+ err = ciigo.HTTPServer.Start()
if err != nil {
return fmt.Errorf(`%s: %w`, logp, err)
}
- if srv.opts.IsDevelopment {
- srv.watcher.stop()
+ if ciigo.serveOpts.IsDevelopment {
+ ciigo.watcher.stop()
}
return nil
}
-func (srv *server) onSearch(epr *libhttp.EndpointRequest) (resBody []byte, err error) {
+func (ciigo *Ciigo) onSearch(epr *libhttp.EndpointRequest) (resBody []byte, err error) {
var (
logp = `onSearch`
@@ -147,9 +142,9 @@ 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)
+ results = ciigo.HTTPServer.Options.Memfs.Search(strings.Fields(q), 0)
- err = srv.converter.tmplSearch.Execute(&buf, results)
+ err = ciigo.converter.tmplSearch.Execute(&buf, results)
if err != nil {
return nil, fmt.Errorf(`%s: %w`, logp, err)
}
@@ -160,7 +155,7 @@ func (srv *server) onSearch(epr *libhttp.EndpointRequest) (resBody []byte, err e
buf.Reset()
- err = srv.converter.tmpl.Execute(&buf, fhtml)
+ err = ciigo.converter.tmpl.Execute(&buf, fhtml)
if err != nil {
return nil, fmt.Errorf(`%s: %w`, logp, err)
}
diff --git a/watcher.go b/watcher.go
index 81d2d12..6568629 100644
--- a/watcher.go
+++ b/watcher.go
@@ -43,7 +43,7 @@ type watcher struct {
// +-- watchHTMLTemplate +--> DELETE --> Converter.htmlTemplateUseInternal()
// |
// +--> UPDATE --> Converter.convertFileMarkups()
-func newWatcher(converter *Converter, convertOpts *ConvertOptions) (w *watcher, err error) {
+func newWatcher(converter *Converter, convertOpts ConvertOptions) (w *watcher, err error) {
var (
logp = `newWatcher`
)
diff --git a/watcher_test.go b/watcher_test.go
index 1f8b2e3..063eb8f 100644
--- a/watcher_test.go
+++ b/watcher_test.go
@@ -36,7 +36,7 @@ func TestWatcher(t *testing.T) {
var testWatcher *watcher
- testWatcher, err = newWatcher(converter, &convertOpts)
+ testWatcher, err = newWatcher(converter, convertOpts)
if err != nil {
t.Fatal(err)
}