diff --git a/cmd/zona/main.go b/cmd/zona/main.go
index b5c4989..488cc5f 100644
--- a/cmd/zona/main.go
+++ b/cmd/zona/main.go
@@ -41,7 +41,7 @@ func main() {
}
}
- settings := builder.GetSettings()
+ settings := builder.GetSettings(*rootPath)
err := builder.Traverse(*rootPath, "foobar", settings)
if err != nil {
fmt.Printf("Error: %s\n", err.Error())
diff --git a/internal/builder/build_page.go b/internal/builder/build_page.go
index 35a601a..39dade2 100644
--- a/internal/builder/build_page.go
+++ b/internal/builder/build_page.go
@@ -74,7 +74,7 @@ func buildPageData(m Metadata, path string, settings *Settings) *PageData {
}
func ConvertFile(in string, out string, settings *Settings) error {
- mdPre, err := ReadFile(in)
+ mdPre, err := util.ReadFile(in)
if err != nil {
return err
}
@@ -86,10 +86,7 @@ func ConvertFile(in string, out string, settings *Settings) error {
fmt.Println("Title: ", pd.Title)
// build according to template here
- html, err := MdToHTML(md)
- if err != nil {
- return err
- }
+ html := MdToHTML(md)
pd.Content = template.HTML(html)
tmpl, err := template.New("webpage").Parse(settings.DefaultTemplate)
@@ -102,6 +99,6 @@ func ConvertFile(in string, out string, settings *Settings) error {
return err
}
- err = WriteFile(output.Bytes(), out)
+ err = util.WriteFile(output.Bytes(), out)
return err
}
diff --git a/internal/builder/config.go b/internal/builder/config.go
index 3059230..e12bf6f 100644
--- a/internal/builder/config.go
+++ b/internal/builder/config.go
@@ -2,68 +2,124 @@ package builder
import (
"embed"
+ "html/template"
+ "log"
+ "path/filepath"
+
+ "github.com/ficcdaf/zona/internal/util"
+ "gopkg.in/yaml.v3"
)
const (
- DefaultHeader = ""
- DefaultFooter = ""
- DefaultStylesheet = "/style/zonaDefault.css"
- DefaultIcon = ""
- DefaultTemplate = `
-
-
- {{ .Title }}
-
-
-
-
-
-
-
-
-
- {{ .Content }}
-
-
-
-
-
-`
+ DefConfigName = "config.yml"
+ DefHeaderName = "header.md"
+ DefFooterName = "footer.md"
+ DefStylesheetName = "style.css"
+ DefIconName = "icon.png"
+ DefTemplateName = "default.html"
)
//go:embed embed
var embedDir embed.FS
type Settings struct {
- Header string
- Footer string
- Stylesheet string
- Icon string
- DefaultTemplate string
+ Header template.HTML
+ HeaderName string
+ Footer template.HTML
+ FooterName string
+ Stylesheet []byte
+ StylesheetName string
+ Icon []byte
+ IconName string
+ DefaultTemplate template.HTML
+ DefaultTemplateName string
}
-func NewSettings(header string, footer string, style string, icon string, temp string) *Settings {
- return &Settings{
- header,
- footer,
- style,
- icon,
- temp,
+func buildSettings(f []byte) (*Settings, error) {
+ s := &Settings{}
+ var c map[string]interface{}
+ // Parse YAML
+ if err := yaml.Unmarshal(f, &c); err != nil {
+ return nil, err
}
+ if headerName, ok := c["header"].(string); ok {
+ header, err := util.ReadFile(headerName)
+ s.HeaderName = headerName
+ if err != nil {
+ return nil, util.ErrorPrepend("Could not read header specified in config: ", err)
+ }
+ s.Header = template.HTML(MdToHTML(header))
+ } else {
+ header := readEmbed(DefHeaderName)
+ s.Header = template.HTML(MdToHTML(header))
+ s.HeaderName = DefHeaderName
+ }
+ if footerName, ok := c["footer"].(string); ok {
+ footer, err := util.ReadFile(footerName)
+ s.FooterName = footerName
+ if err != nil {
+ return nil, util.ErrorPrepend("Could not read footer specified in config: ", err)
+ }
+ s.Footer = template.HTML(MdToHTML(footer))
+ } else {
+ footer := readEmbed(DefFooterName)
+ s.Footer = template.HTML(MdToHTML(footer))
+ s.FooterName = DefFooterName
+ }
+ if stylesheetName, ok := c["stylesheet"].(string); ok {
+ stylesheet, err := util.ReadFile(stylesheetName)
+ if err != nil {
+ return nil, util.ErrorPrepend("Could not read stylesheet specified in config: ", err)
+ }
+ s.StylesheetName = stylesheetName
+ s.Stylesheet = stylesheet
+ } else {
+ stylesheet := readEmbed(DefStylesheetName)
+ s.Stylesheet = stylesheet
+ s.StylesheetName = DefStylesheetName
+ }
+ if iconName, ok := c["icon"].(string); ok {
+ icon, err := util.ReadFile(iconName)
+ if err != nil {
+ return nil, util.ErrorPrepend("Could not read icon specified in config: ", err)
+ }
+ s.Icon = icon
+ s.IconName = iconName
+ } else {
+ icon := readEmbed(DefIconName)
+ s.Icon = icon
+ s.IconName = DefIconName
+ }
+ if
+
+ return s, nil
}
-func GetSettings() *Settings {
+// readEmbed reads a file inside the embedded dir
+func readEmbed(name string) []byte {
+ f, err := embedDir.ReadFile(name)
+ if err != nil {
+ log.Fatalln("Fatal internal error: Could not read embedded default config!")
+ }
+ return f
+}
+
+func GetSettings(root string) *Settings {
// TODO: Read a config file to override defaults
// "Defaults" should be a default config file via embed package,
// so the settings func should need to handle one case:
// check if config file exists, if not, use embedded one
- return NewSettings(DefaultHeader, DefaultFooter, DefaultStylesheet, DefaultIcon, DefaultTemplate)
+ var config []byte
+ configPath := filepath.Join(root, DefConfigName)
+ if !util.FileExists(configPath) {
+ // Config file does not exist, we used embedded default
+ config = readEmbed(configPath)
+ } else {
+ config, err := util.ReadFile(configPath)
+ if err != nil {
+ log.Fatalln("Fatal internal error: Config file exists but could not be read!")
+ }
+ }
+
+ // return NewSettings(DefaultHeader, DefaultFooter, DefaultStylesheet, DefaultIcon, DefaultTemplate)
}
diff --git a/internal/builder/convert.go b/internal/builder/convert.go
index 7c032b7..ed72fbd 100644
--- a/internal/builder/convert.go
+++ b/internal/builder/convert.go
@@ -14,7 +14,7 @@ import (
)
// This function takes a Markdown document and returns an HTML document.
-func MdToHTML(md []byte) ([]byte, error) {
+func MdToHTML(md []byte) []byte {
// create parser with extensions
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
p := parser.NewWithExtensions(extensions)
@@ -25,7 +25,7 @@ func MdToHTML(md []byte) ([]byte, error) {
opts := html.RendererOptions{Flags: htmlFlags}
renderer := newZonaRenderer(opts)
- return markdown.Render(doc, renderer), nil
+ return markdown.Render(doc, renderer)
}
// PathIsValid checks if a path is valid.
@@ -78,55 +78,3 @@ func newZonaRenderer(opts html.RendererOptions) *html.Renderer {
opts.RenderNodeHook = htmlRenderHook
return html.NewRenderer(opts)
}
-
-// WriteFile writes a given byte array to the given path.
-func WriteFile(b []byte, p string) error {
- f, err := os.Create(p)
- if err != nil {
- return err
- }
- _, err = f.Write(b)
- defer f.Close()
- if err != nil {
- return err
- }
- return nil
-}
-
-// ReadFile reads a byte array from a given path.
-func ReadFile(p string) ([]byte, error) {
- f, err := os.Open(p)
- if err != nil {
- return nil, err
- }
- var result []byte
- buf := make([]byte, 1024)
- for {
- n, err := f.Read(buf)
- // check for a non EOF error
- if err != nil && err != io.EOF {
- return nil, err
- }
- // n==0 when there are no chunks left to read
- if n == 0 {
- defer f.Close()
- break
- }
- result = append(result, buf[:n]...)
- }
- return result, nil
-}
-
-// CopyFile reads the file at the input path, and write
-// it to the output path.
-func CopyFile(inPath string, outPath string) error {
- inB, err := ReadFile(inPath)
- if err != nil {
- return err
- }
- if err := WriteFile(inB, outPath); err != nil {
- return err
- } else {
- return nil
- }
-}
diff --git a/internal/builder/traverse.go b/internal/builder/traverse.go
index 44f493f..5272dca 100644
--- a/internal/builder/traverse.go
+++ b/internal/builder/traverse.go
@@ -33,7 +33,7 @@ func processFile(inPath string, entry fs.DirEntry, err error, outRoot string, se
if err := util.CreateParents(outPath); err != nil {
return err
}
- if err := CopyFile(inPath, outPath); err != nil {
+ if err := util.CopyFile(inPath, outPath); err != nil {
return errors.Join(errors.New("Error processing file "+inPath), err)
} else {
return nil
diff --git a/internal/util/file.go b/internal/util/file.go
new file mode 100644
index 0000000..2028018
--- /dev/null
+++ b/internal/util/file.go
@@ -0,0 +1,58 @@
+package util
+
+import (
+ "io"
+ "os"
+)
+
+// WriteFile writes a given byte array to the given path.
+func WriteFile(b []byte, p string) error {
+ f, err := os.Create(p)
+ if err != nil {
+ return err
+ }
+ _, err = f.Write(b)
+ defer f.Close()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// ReadFile reads a byte array from a given path.
+func ReadFile(p string) ([]byte, error) {
+ f, err := os.Open(p)
+ if err != nil {
+ return nil, err
+ }
+ var result []byte
+ buf := make([]byte, 1024)
+ for {
+ n, err := f.Read(buf)
+ // check for a non EOF error
+ if err != nil && err != io.EOF {
+ return nil, err
+ }
+ // n==0 when there are no chunks left to read
+ if n == 0 {
+ defer f.Close()
+ break
+ }
+ result = append(result, buf[:n]...)
+ }
+ return result, nil
+}
+
+// CopyFile reads the file at the input path, and write
+// it to the output path.
+func CopyFile(inPath string, outPath string) error {
+ inB, err := ReadFile(inPath)
+ if err != nil {
+ return err
+ }
+ if err := WriteFile(inB, outPath); err != nil {
+ return err
+ } else {
+ return nil
+ }
+}
diff --git a/internal/util/path.go b/internal/util/path.go
index e53c9b7..145bc0e 100644
--- a/internal/util/path.go
+++ b/internal/util/path.go
@@ -40,6 +40,13 @@ func ReplaceRoot(inPath, outRoot string) string {
return outPath
}
+// FileExists returns a boolean indicating
+// whether something exists at the path.
+func FileExists(path string) bool {
+ _, err := os.Stat(path)
+ return !os.IsNotExist(err)
+}
+
func CreateParents(path string) error {
dir := filepath.Dir(path)
// Check if the parent directory already exists
diff --git a/internal/util/util.go b/internal/util/util.go
index 2894192..7dc11dd 100644
--- a/internal/util/util.go
+++ b/internal/util/util.go
@@ -1,6 +1,9 @@
package util
-import "strings"
+import (
+ "errors"
+ "strings"
+)
func NormalizeContent(content string) string {
var normalized []string
@@ -13,3 +16,8 @@ func NormalizeContent(content string) string {
}
return strings.Join(normalized, "\n")
}
+
+// ErrorPrepend returns a new error with a message prepended to the given error.
+func ErrorPrepend(m string, err error) error {
+ return errors.Join(errors.New(m), err)
+}