continue working on config and default parsing
This commit is contained in:
parent
4d1b18fd12
commit
c6c801e248
8 changed files with 183 additions and 109 deletions
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 = `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ .Title }}</title>
|
||||
<link rel="icon" href="{{ .Icon }}" type="image/x-icon" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta charset="UTF-8" />
|
||||
<link
|
||||
href="{{ .Stylesheet }}"
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
media="all"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<header id="header">{{ .Header }}</header>
|
||||
<article id="content">
|
||||
{{ .Content }}
|
||||
<nav id="nextprev">
|
||||
{{ .NextPost }}<br />
|
||||
{{ .PrevPost }}
|
||||
</nav>
|
||||
</article>
|
||||
<footer id="footer">{{ .Footer }}</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>`
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
58
internal/util/file.go
Normal file
58
internal/util/file.go
Normal file
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue