"strings"
"time"
+ "github.com/eknkc/amber"
"github.com/russross/blackfriday"
+ "github.com/yosssi/gcss"
)
const (
PUBDIR = ".pub"
)
-type EvalFn func(args []string, vars map[string]string) (string, error)
+type Vars map[string]string
+type EvalFn func(args []string, vars Vars) (string, error)
+
+// Splits a string in exactly two parts by delimiter
+// If no delimiter is found - the second string is be empty
func split2(s, delim string) (string, string) {
parts := strings.SplitN(s, delim, 2)
if len(parts) == 2 {
}
}
-func md(path, s string) (map[string]string, string) {
+// Parses markdown content. Returns parsed header variables and content
+func md(path string) (Vars, string, error) {
+ b, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, "", err
+ }
+ s := string(b)
url := path[:len(path)-len(filepath.Ext(path))] + ".html"
- v := map[string]string{
+ v := Vars{
"file": path,
"url": url,
"output": filepath.Join(PUBDIR, url),
"layout": "index.html",
}
if strings.Index(s, "\n\n") == -1 {
- return map[string]string{}, s
+ return Vars{}, s, nil
}
header, body := split2(s, "\n\n")
for _, line := range strings.Split(header, "\n") {
if strings.HasPrefix(v["url"], "./") {
v["url"] = v["url"][2:]
}
- return v, body
+ return v, body, nil
}
-func render(s string, vars map[string]string, eval EvalFn) (string, error) {
+func render(s string, vars Vars, eval EvalFn) (string, error) {
delim_open := "{{"
delim_close := "}}"
}
}
-func env(vars map[string]string) []string {
+// Converts zs markdown variables into environment variables
+func env(vars Vars) []string {
env := []string{"ZS=" + os.Args[0], "ZS_OUTDIR=" + PUBDIR}
env = append(env, os.Environ()...)
if vars != nil {
return env
}
-func run(cmd string, args []string, vars map[string]string, output io.Writer) error {
+// Runs command with given arguments and variables, intercepts stderr and
+// redirects stdout into the given writer
+func run(cmd string, args []string, vars Vars, output io.Writer) error {
var errbuf bytes.Buffer
c := exec.Command(cmd, args...)
c.Env = env(vars)
return nil
}
-func eval(cmd []string, vars map[string]string) (string, error) {
+func eval(cmd []string, vars Vars) (string, error) {
outbuf := bytes.NewBuffer(nil)
err := run(path.Join(ZSDIR, cmd[0]), cmd[1:], vars, outbuf)
if err != nil {
}
func buildMarkdown(path string) error {
- b, err := ioutil.ReadFile(path)
+ v, body, err := md(path)
if err != nil {
return err
}
- v, body := md(path, string(b))
content, err := render(body, v, eval)
if err != nil {
return err
return buildPlain(filepath.Join(ZSDIR, v["layout"]), v)
}
-func buildPlain(path string, vars map[string]string) error {
+func buildPlain(path string, vars Vars) error {
b, err := ioutil.ReadFile(path)
if err != nil {
return err
return nil
}
+func buildGCSS(path string) error {
+ f, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ s := strings.TrimSuffix(path, ".gcss") + ".css"
+ log.Println(s)
+ css, err := os.Create(filepath.Join(PUBDIR, s))
+ if err != nil {
+ return err
+ }
+
+ defer f.Close()
+ defer css.Close()
+
+ _, err = gcss.Compile(css, f)
+ return err
+}
+
+func buildAmber(path string, vars Vars) error {
+ a := amber.New()
+ err := a.ParseFile(path)
+ if err != nil {
+ return err
+ }
+ t, err := a.Compile()
+ if err != nil {
+ return err
+ }
+ //amber.FuncMap = amber.FuncMap
+ s := strings.TrimSuffix(path, ".amber") + ".html"
+ f, err := os.Create(filepath.Join(PUBDIR, s))
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return t.Execute(f, vars)
+}
+
func copyFile(path string) (err error) {
var in, out *os.File
if in, err = os.Open(path); err == nil {
}
ext := filepath.Ext(path)
if ext == ".md" || ext == ".mkd" {
- log.Println("mkd: ", path)
+ log.Println("md: ", path)
return buildMarkdown(path)
} else if ext == ".html" || ext == ".xml" {
- return buildPlain(path, map[string]string{})
+ log.Println("html: ", path)
+ return buildPlain(path, Vars{})
+ } else if ext == ".amber" {
+ log.Println("html: ", path)
+ return buildAmber(path, Vars{})
+ } else if ext == ".gcss" {
+ log.Println("css: ", path)
+ return buildGCSS(path)
} else {
log.Println("raw: ", path)
return copyFile(path)
log.Println("ERROR: filename expected")
return
}
- if b, err := ioutil.ReadFile(args[0]); err == nil {
- vars, _ := md(args[0], string(b))
+ if vars, _, err := md(args[0]); err == nil {
if len(args) > 1 {
for _, a := range args[1:] {
fmt.Println(vars[a])
log.Println(err)
}
default:
- err := run(path.Join(ZSDIR, cmd), args, map[string]string{}, os.Stdout)
+ err := run(path.Join(ZSDIR, cmd), args, Vars{}, os.Stdout)
if err != nil {
log.Println(err)
}
import (
"bytes"
"fmt"
+ "io/ioutil"
"log"
"os"
"os/exec"
}
}
+func tmpfile(path, s string) string {
+ ioutil.WriteFile(path, []byte(s), 0644)
+ return path
+}
+
func TestMD(t *testing.T) {
- v, body := md("foo.md", `
+ defer os.Remove("foo.md")
+ v, body, _ := md(tmpfile("foo.md", `
title: Hello, world!
keywords: foo, bar, baz
empty:
bayan: [:|||:]
-this: is a content`)
+this: is a content`))
if v["title"] != "Hello, world!" {
t.Error()
}
}
// Test empty md
- v, body = md("foo.md", "")
+ v, body, _ = md(tmpfile("foo.md", ""))
if len(v) != 0 || len(body) != 0 {
t.Error(v, body)
}
// Test empty header
- v, body = md("foo.md", "Hello")
+ v, body, _ = md(tmpfile("foo.md", "Hello"))
if len(v) != 0 || body != "Hello" {
t.Error(v, body)
}
}
func TestRender(t *testing.T) {
- eval := func(a []string, vars map[string]string) (string, error) {
+ eval := func(a []string, vars Vars) (string, error) {
return "hello", nil
}
vars := map[string]string{"foo": "bar"}