Mercurial > yakumo_izuru > aya
annotate zs.go @ 15:a9c42bd52f64 draft
added rendering support for html and xml
author | zaitsev.serge |
---|---|
date | Sat, 29 Aug 2015 13:17:13 +0000 |
parents | 515078352442 |
children | 0214b1b5f5eb |
rev | line source |
---|---|
0 | 1 package main |
2 | |
3 import ( | |
4 "bytes" | |
5 "fmt" | |
6 "io" | |
7 "io/ioutil" | |
8 "log" | |
9 "os" | |
10 "os/exec" | |
11 "path" | |
12 "path/filepath" | |
13 "strings" | |
14 "time" | |
15 | |
16 "github.com/russross/blackfriday" | |
17 ) | |
18 | |
19 const ( | |
20 ZSDIR = ".zs" | |
21 PUBDIR = ".pub" | |
22 ) | |
23 | |
3
53dea9841cd9
moved eval func type to the top, added some error logs
zaitsev.serge
parents:
2
diff
changeset
|
24 type EvalFn func(args []string, vars map[string]string) (string, error) |
53dea9841cd9
moved eval func type to the top, added some error logs
zaitsev.serge
parents:
2
diff
changeset
|
25 |
0 | 26 func split2(s, delim string) (string, string) { |
27 parts := strings.SplitN(s, delim, 2) | |
28 if len(parts) == 2 { | |
29 return parts[0], parts[1] | |
30 } else { | |
31 return parts[0], "" | |
32 } | |
33 } | |
34 | |
6 | 35 func md(path, s string) (map[string]string, string) { |
36 url := path[:len(path)-len(filepath.Ext(path))] + ".html" | |
37 v := map[string]string{ | |
38 "file": path, | |
39 "url": url, | |
40 "output": filepath.Join(PUBDIR, url), | |
41 "layout": "index.html", | |
42 } | |
2 | 43 if strings.Index(s, "\n\n") == -1 { |
44 return map[string]string{}, s | |
45 } | |
0 | 46 header, body := split2(s, "\n\n") |
47 for _, line := range strings.Split(header, "\n") { | |
48 key, value := split2(line, ":") | |
49 v[strings.ToLower(strings.TrimSpace(key))] = strings.TrimSpace(value) | |
50 } | |
6 | 51 if strings.HasPrefix(v["url"], "./") { |
52 v["url"] = v["url"][2:] | |
53 } | |
0 | 54 return v, body |
55 } | |
56 | |
57 func render(s string, vars map[string]string, eval EvalFn) (string, error) { | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
58 delim_open := "{{" |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
59 delim_close := "}}" |
0 | 60 |
61 out := bytes.NewBuffer(nil) | |
62 for { | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
63 if from := strings.Index(s, delim_open); from == -1 { |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
64 out.WriteString(s) |
0 | 65 return out.String(), nil |
66 } else { | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
67 if to := strings.Index(s, delim_close); to == -1 { |
0 | 68 return "", fmt.Errorf("Close delim not found") |
69 } else { | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
70 out.WriteString(s[:from]) |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
71 cmd := s[from+len(delim_open) : to] |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
72 s = s[to+len(delim_close):] |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
73 m := strings.Fields(cmd) |
0 | 74 if len(m) == 1 { |
75 if v, ok := vars[m[0]]; ok { | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
76 out.WriteString(v) |
0 | 77 continue |
78 } | |
79 } | |
80 if res, err := eval(m, vars); err == nil { | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
81 out.WriteString(res) |
0 | 82 } else { |
83 log.Println(err) // silent | |
84 } | |
85 } | |
86 } | |
87 } | |
88 } | |
89 | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
90 func env(vars map[string]string) []string { |
7
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
91 env := []string{"ZS=" + os.Args[0], "ZS_OUTDIR=" + PUBDIR} |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
92 env = append(env, os.Environ()...) |
7
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
93 if vars != nil { |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
94 for k, v := range vars { |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
95 env = append(env, "ZS_"+strings.ToUpper(k)+"="+v) |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
96 } |
0 | 97 } |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
98 return env |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
99 } |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
100 |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
101 func run(cmd string, args []string, vars map[string]string, output io.Writer) error { |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
102 var errbuf bytes.Buffer |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
103 c := exec.Command(cmd, args...) |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
104 c.Env = env(vars) |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
105 c.Stdout = output |
0 | 106 c.Stderr = &errbuf |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
107 |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
108 err := c.Run() |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
109 |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
110 if errbuf.Len() > 0 { |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
111 log.Println(errbuf.String()) |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
112 } |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
113 |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
114 if err != nil { |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
115 return err |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
116 } |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
117 return nil |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
118 } |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
119 |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
120 func eval(cmd []string, vars map[string]string) (string, error) { |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
121 outbuf := bytes.NewBuffer(nil) |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
122 err := run(path.Join(ZSDIR, cmd[0]), cmd[1:], vars, outbuf) |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
123 if err != nil { |
5 | 124 if _, ok := err.(*exec.ExitError); ok { |
125 return "", err | |
126 } | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
127 outbuf = bytes.NewBuffer(nil) |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
128 err := run(cmd[0], cmd[1:], vars, outbuf) |
5 | 129 // Return exit errors, but ignore if the command was not found |
130 if _, ok := err.(*exec.ExitError); ok { | |
0 | 131 return "", err |
132 } | |
133 } | |
134 return outbuf.String(), nil | |
135 } | |
136 | |
137 func buildMarkdown(path string) error { | |
138 b, err := ioutil.ReadFile(path) | |
139 if err != nil { | |
140 return err | |
141 } | |
6 | 142 v, body := md(path, string(b)) |
0 | 143 content, err := render(body, v, eval) |
144 if err != nil { | |
145 return err | |
146 } | |
147 v["content"] = string(blackfriday.MarkdownBasic([]byte(content))) | |
14 | 148 return buildPlain(filepath.Join(ZSDIR, v["layout"]), v) |
149 } | |
150 | |
151 func buildPlain(path string, vars map[string]string) error { | |
152 b, err := ioutil.ReadFile(path) | |
0 | 153 if err != nil { |
154 return err | |
155 } | |
14 | 156 content, err := render(string(b), vars, eval) |
0 | 157 if err != nil { |
158 return err | |
159 } | |
14 | 160 output := filepath.Join(PUBDIR, path) |
161 if s, ok := vars["output"]; ok { | |
162 output = s | |
163 } | |
164 err = ioutil.WriteFile(output, []byte(content), 0666) | |
0 | 165 if err != nil { |
166 return err | |
167 } | |
168 return nil | |
169 } | |
170 | |
7
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
171 func copyFile(path string) (err error) { |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
172 var in, out *os.File |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
173 if in, err = os.Open(path); err == nil { |
0 | 174 defer in.Close() |
7
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
175 if out, err = os.Create(filepath.Join(PUBDIR, path)); err == nil { |
0 | 176 defer out.Close() |
177 _, err = io.Copy(out, in) | |
178 } | |
179 } | |
7
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
180 return err |
0 | 181 } |
182 | |
183 func buildAll(once bool) { | |
184 lastModified := time.Unix(0, 0) | |
7
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
185 modified := false |
0 | 186 for { |
187 os.Mkdir(PUBDIR, 0755) | |
188 err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error { | |
189 // ignore hidden files and directories | |
190 if filepath.Base(path)[0] == '.' || strings.HasPrefix(path, ".") { | |
191 return nil | |
192 } | |
193 | |
7
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
194 if info.IsDir() { |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
195 os.Mkdir(filepath.Join(PUBDIR, path), 0755) |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
196 return nil |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
197 } else if info.ModTime().After(lastModified) { |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
198 if !modified { |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
199 // About to be modified, so run pre-build hook |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
200 run(filepath.Join(ZSDIR, "pre"), []string{}, nil, nil) |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
201 modified = true |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
202 } |
0 | 203 ext := filepath.Ext(path) |
13 | 204 if ext == ".md" || ext == ".mkd" { |
0 | 205 log.Println("mkd: ", path) |
206 return buildMarkdown(path) | |
15 | 207 } else if ext == ".html" || ext == ".xml" { |
208 return buildPlain(path, map[string]string{}) | |
0 | 209 } else { |
210 log.Println("raw: ", path) | |
211 return copyFile(path) | |
212 } | |
213 } | |
214 return nil | |
215 }) | |
216 if err != nil { | |
217 log.Println("ERROR:", err) | |
218 } | |
7
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
219 if modified { |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
220 // Something was modified, so post-build hook |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
221 run(filepath.Join(ZSDIR, "post"), []string{}, nil, nil) |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
222 modified = false |
11073a30f71f
simplified build process, added pre and post build hooks
zaitsev.serge
parents:
6
diff
changeset
|
223 } |
0 | 224 lastModified = time.Now() |
225 if once { | |
226 break | |
227 } | |
228 time.Sleep(1 * time.Second) | |
229 } | |
230 } | |
231 | |
232 func main() { | |
233 if len(os.Args) == 1 { | |
234 fmt.Println(os.Args[0], "<command> [args]") | |
235 return | |
236 } | |
237 cmd := os.Args[1] | |
238 args := os.Args[2:] | |
239 switch cmd { | |
240 case "build": | |
241 buildAll(true) | |
242 case "watch": | |
243 buildAll(false) // pass duration | |
244 case "var": | |
245 if len(args) == 0 { | |
3
53dea9841cd9
moved eval func type to the top, added some error logs
zaitsev.serge
parents:
2
diff
changeset
|
246 log.Println("ERROR: filename expected") |
0 | 247 return |
248 } | |
249 if b, err := ioutil.ReadFile(args[0]); err == nil { | |
6 | 250 vars, _ := md(args[0], string(b)) |
0 | 251 if len(args) > 1 { |
252 for _, a := range args[1:] { | |
253 fmt.Println(vars[a]) | |
254 } | |
255 } else { | |
256 for k, v := range vars { | |
257 fmt.Println(k + ":" + v) | |
258 } | |
259 } | |
260 } else { | |
3
53dea9841cd9
moved eval func type to the top, added some error logs
zaitsev.serge
parents:
2
diff
changeset
|
261 log.Println(err) |
0 | 262 } |
263 default: | |
4
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
264 err := run(path.Join(ZSDIR, cmd), args, map[string]string{}, os.Stdout) |
05fc24caac37
render uses strings, not bytes; added helpers for env and run
zaitsev.serge
parents:
3
diff
changeset
|
265 if err != nil { |
0 | 266 log.Println(err) |
267 } | |
268 } | |
269 } |