From c6c801e24859d113e6dfb201d7b5f77a646083e1 Mon Sep 17 00:00:00 2001 From: Daniel Fichtinger Date: Mon, 25 Nov 2024 16:05:35 -0500 Subject: [PATCH] continue working on config and default parsing --- cmd/zona/main.go | 2 +- internal/builder/build_page.go | 9 +- internal/builder/config.go | 148 +++++++++++++++++++++++---------- internal/builder/convert.go | 56 +------------ internal/builder/traverse.go | 2 +- internal/util/file.go | 58 +++++++++++++ internal/util/path.go | 7 ++ internal/util/util.go | 10 ++- 8 files changed, 183 insertions(+), 109 deletions(-) create mode 100644 internal/util/file.go 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) +}