summaryrefslogtreecommitdiff
path: root/ssg.go
diff options
context:
space:
mode:
authorJulian Hurst <ark@mansus.space>2025-09-29 18:50:00 +0200
committerJulian Hurst <ark@mansus.space>2025-09-29 18:50:00 +0200
commite3552f592ba5bce3fa3243166679fed986b6b949 (patch)
treea0eca31771a17dc955d769862d7bdd12d8100f0c /ssg.go
downloadssg-e3552f592ba5bce3fa3243166679fed986b6b949.tar.gz
Initial commit
Diffstat (limited to 'ssg.go')
-rw-r--r--ssg.go261
1 files changed, 261 insertions, 0 deletions
diff --git a/ssg.go b/ssg.go
new file mode 100644
index 0000000..743369d
--- /dev/null
+++ b/ssg.go
@@ -0,0 +1,261 @@
+package main
+
+import (
+ "slices"
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "strings"
+ "path/filepath"
+ //"flag"
+ "html/template"
+ "time"
+)
+
+type post struct {
+ InPath string
+ OutPath string
+ ModTime time.Time
+}
+
+type ParsedPost struct {
+ Path string
+ Metadata PostMetadata
+}
+
+type PostMetadata struct {
+ Name string
+ Description string
+}
+
+func buildTemplate(f string, w io.Writer, data interface{}) error {
+ tmpl, err := template.ParseFiles("base.html", "head.html", f)
+ if err != nil {
+ return err
+ }
+ err = tmpl.Execute(w, data)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func main() {
+ args := os.Args
+ if len(args) != 2 {
+ fmt.Fprintf(os.Stderr, "USAGE: %s [postsfolder]", args[0])
+ os.Exit(1)
+ }
+
+
+ postsOutDir := "public/posts/"
+
+ err := os.MkdirAll(postsOutDir, 0o777)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ f, err := os.Open(args[1])
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ defer f.Close()
+
+ entries, err := f.ReadDir(0)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ var files []post = nil
+ for _, entry := range entries {
+ name := entry.Name()
+ outName := strings.TrimSuffix(name, filepath.Ext(name)) + ".html"
+ fi, err := entry.Info()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ p := post {
+ InPath: filepath.Join(args[1], name),
+ OutPath: filepath.Join(postsOutDir, outName),
+ ModTime: fi.ModTime(),
+ }
+ files = append(files, p)
+ }
+
+ slices.SortFunc(files, func(a, b post) int {
+ if a.ModTime.After(b.ModTime) {
+ return -1
+ } else {
+ return 1
+ }
+ })
+
+ pposts, err := parseMetadata(files)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ err = md2html(files)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ idx, err := os.Create("public/index.html")
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ //var data []string = nil
+ //for _, post := range files {
+ // data = append(data, filepath.Join("posts", filepath.Base(post.OutPath)))
+ //}
+ for _, ppost := range pposts {
+ fmt.Fprintf(os.Stderr, "name: %s, description: %s\n", ppost.Metadata.Name, ppost.Metadata.Description)
+ }
+ err = buildTemplate("index.html", idx, pposts)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+}
+
+func parseMetadata(files []post) ([]ParsedPost, error) {
+ var pposts []ParsedPost = nil
+ for _, file := range files {
+
+ f, err := os.Open(file.InPath)
+ if err != nil {
+ return pposts, err
+ }
+ defer f.Close()
+
+ sc := bufio.NewScanner(f)
+
+ lnb := 1
+ var metadata PostMetadata = PostMetadata {}
+ for sc.Scan() {
+ line := sc.Text()
+ if line == "----" {
+ break
+ }
+ spl := strings.Split(line, "=")
+ if len(spl) != 2 {
+ return pposts, fmt.Errorf("Invalid Metadata: %s:%d", file.InPath, lnb)
+ }
+ switch spl[0] {
+ case "name":
+ metadata.Name = spl[1]
+ case "description":
+ metadata.Description = spl[1]
+ default:
+ fmt.Fprintf(os.Stderr, "Invalid key: %s (%s:%d)\n", spl[0], file.InPath, lnb)
+ }
+ lnb += 1
+ }
+ pposts = append(pposts, ParsedPost {
+ Path: filepath.Join("posts", filepath.Base(file.OutPath)),
+ Metadata: metadata,
+ })
+ }
+ return pposts, nil
+}
+
+func md2html(files []post) error {
+ for _, file := range files {
+ //err = os.WriteFile(file.OutPath, out, 0600)
+ //if err != nil {
+ // return err
+ //}
+ out, err := execcmd(file)
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(out))
+ tmpl, err := template.New("base.html").ParseFiles("base.html", "head.html")
+ if err != nil {
+ return err
+ }
+ _, err = tmpl.New("body").Parse(string(out))
+ if err != nil {
+ return err
+ }
+ f, err := os.Create(file.OutPath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ err = tmpl.Execute(f, nil)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func execcmd(file post) ([]byte, error) {
+ cmd := exec.Command("converter/md2html", file.InPath)
+
+ inPipe, err := cmd.StdinPipe()
+ if err != nil {
+ return nil, err
+ }
+ outPipe, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ errPipe, err := cmd.StderrPipe()
+ if err != nil {
+ return nil, err
+ }
+
+
+ go func() {
+ defer inPipe.Close()
+ f, _ := os.Open(file.InPath)
+ defer f.Close()
+ sc := bufio.NewScanner(f)
+ doPrint := false
+ for sc.Scan() {
+ line := sc.Text()
+ if doPrint {
+ io.WriteString(inPipe, line + "\n")
+ }
+ if line == "----" {
+ doPrint = true
+ }
+ }
+ io.Copy(inPipe, f)
+ }()
+
+ fmt.Fprintln(os.Stderr, "stdout")
+
+ var out []byte = nil
+ go func() {
+ out, _ = io.ReadAll(outPipe)
+ }()
+
+ fmt.Fprintln(os.Stderr, "stderr")
+
+ var cmdErr []byte = nil
+ go func() {
+ cmdErr, _ = io.ReadAll(errPipe)
+ }()
+
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+
+ if err := cmd.Wait(); err != nil {
+ return nil, fmt.Errorf("%s\nstderr: %s\n", err, string(cmdErr))
+ }
+ return out, nil
+}