Mercurial > yakumo_izuru > aya
comparison zs.go @ 0:45a9e1fac18e draft
initial commit
author | zaitsev.serge |
---|---|
date | Fri, 05 Dec 2014 16:59:56 +0000 |
parents | |
children | fd79b3a90bef |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:45a9e1fac18e |
---|---|
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 | |
24 func split2(s, delim string) (string, string) { | |
25 parts := strings.SplitN(s, delim, 2) | |
26 if len(parts) == 2 { | |
27 return parts[0], parts[1] | |
28 } else { | |
29 return parts[0], "" | |
30 } | |
31 } | |
32 | |
33 func md(s string) (map[string]string, string) { | |
34 v := map[string]string{} | |
35 // FIXME: if no header? | |
36 header, body := split2(s, "\n\n") | |
37 for _, line := range strings.Split(header, "\n") { | |
38 key, value := split2(line, ":") | |
39 v[strings.ToLower(strings.TrimSpace(key))] = strings.TrimSpace(value) | |
40 } | |
41 return v, body | |
42 } | |
43 | |
44 type EvalFn func(args []string, vars map[string]string) (string, error) | |
45 | |
46 func render(s string, vars map[string]string, eval EvalFn) (string, error) { | |
47 b := []byte(s) | |
48 delim_open := []byte("{{") | |
49 delim_close := []byte("}}") | |
50 | |
51 out := bytes.NewBuffer(nil) | |
52 for { | |
53 if from := bytes.Index(b, delim_open); from == -1 { | |
54 out.Write(b) | |
55 return out.String(), nil | |
56 } else { | |
57 to := bytes.Index(b, delim_close) | |
58 if to == -1 { | |
59 return "", fmt.Errorf("Close delim not found") | |
60 } else { | |
61 out.Write(b[:from]) | |
62 cmd := b[from+len(delim_open) : to] | |
63 b = b[to+len(delim_close):] | |
64 m := strings.Fields(string(cmd)) | |
65 if len(m) == 1 { | |
66 if v, ok := vars[m[0]]; ok { | |
67 out.Write([]byte(v)) | |
68 continue | |
69 } | |
70 } | |
71 if res, err := eval(m, vars); err == nil { | |
72 out.Write([]byte(res)) | |
73 } else { | |
74 log.Println(err) // silent | |
75 } | |
76 } | |
77 } | |
78 } | |
79 } | |
80 | |
81 func eval(cmd []string, vars map[string]string) (string, error) { | |
82 var outbuf, errbuf bytes.Buffer | |
83 c := exec.Command(path.Join(ZSDIR, cmd[0]), cmd[1:]...) | |
84 env := []string{"ZS=" + os.Args[0]} | |
85 for k, v := range vars { | |
86 env = append(env, "ZS_"+strings.ToUpper(k)+"="+v) | |
87 } | |
88 c.Env = append(c.Env, env...) | |
89 c.Stdout = &outbuf | |
90 c.Stderr = &errbuf | |
91 if err := c.Run(); err != nil { | |
92 log.Println(err) | |
93 c := exec.Command(path.Join(cmd[0]), cmd[1:]...) | |
94 c.Env = append(c.Env, env...) | |
95 c.Stdout = &outbuf | |
96 c.Stderr = &errbuf | |
97 if err := c.Run(); err != nil { | |
98 return "", err | |
99 } | |
100 } | |
101 if errbuf.Len() > 0 { | |
102 log.Println(errbuf.String()) | |
103 } | |
104 return outbuf.String(), nil | |
105 } | |
106 | |
107 func buildMarkdown(path string) error { | |
108 b, err := ioutil.ReadFile(path) | |
109 if err != nil { | |
110 return err | |
111 } | |
112 v, body := md(string(b)) | |
113 defaultVars(v, path) | |
114 content, err := render(body, v, eval) | |
115 if err != nil { | |
116 return err | |
117 } | |
118 v["content"] = string(blackfriday.MarkdownBasic([]byte(content))) | |
119 b, err = ioutil.ReadFile(filepath.Join(ZSDIR, v["layout"])) | |
120 if err != nil { | |
121 return err | |
122 } | |
123 content, err = render(string(b), v, eval) | |
124 if err != nil { | |
125 return err | |
126 } | |
127 err = ioutil.WriteFile(v["output"], []byte(content), 0666) | |
128 if err != nil { | |
129 return err | |
130 } | |
131 return nil | |
132 } | |
133 | |
134 func defaultVars(vars map[string]string, path string) { | |
135 if _, ok := vars["file"]; !ok { | |
136 vars["file"] = path | |
137 } | |
138 if _, ok := vars["url"]; !ok { | |
139 vars["url"] = path[:len(path)-len(filepath.Ext(path))] + ".html" | |
140 if strings.HasPrefix(vars["url"], "./") { | |
141 vars["url"] = vars["url"][2:] | |
142 } | |
143 } | |
144 if _, ok := vars["outdir"]; !ok { | |
145 vars["outdir"] = PUBDIR | |
146 } | |
147 if _, ok := vars["output"]; !ok { | |
148 vars["output"] = filepath.Join(PUBDIR, vars["url"]) | |
149 } | |
150 if _, ok := vars["layout"]; !ok { | |
151 vars["layout"] = "index.html" | |
152 } | |
153 } | |
154 | |
155 func copyFile(path string) error { | |
156 if in, err := os.Open(path); err != nil { | |
157 return err | |
158 } else { | |
159 defer in.Close() | |
160 if stat, err := in.Stat(); err != nil { | |
161 return err | |
162 } else { | |
163 // Directory? | |
164 if stat.Mode().IsDir() { | |
165 os.Mkdir(filepath.Join(PUBDIR, path), 0755) | |
166 return nil | |
167 } | |
168 if !stat.Mode().IsRegular() { | |
169 return nil | |
170 } | |
171 } | |
172 if out, err := os.Create(filepath.Join(PUBDIR, path)); err != nil { | |
173 return err | |
174 } else { | |
175 defer out.Close() | |
176 _, err = io.Copy(out, in) | |
177 return err | |
178 } | |
179 } | |
180 } | |
181 | |
182 func buildAll(once bool) { | |
183 lastModified := time.Unix(0, 0) | |
184 for { | |
185 os.Mkdir(PUBDIR, 0755) | |
186 err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error { | |
187 // ignore hidden files and directories | |
188 if filepath.Base(path)[0] == '.' || strings.HasPrefix(path, ".") { | |
189 return nil | |
190 } | |
191 | |
192 if info.ModTime().After(lastModified) { | |
193 ext := filepath.Ext(path) | |
194 if ext == ".md" || ext == "mkd" { | |
195 log.Println("mkd: ", path) | |
196 return buildMarkdown(path) | |
197 } else { | |
198 log.Println("raw: ", path) | |
199 return copyFile(path) | |
200 } | |
201 } | |
202 return nil | |
203 }) | |
204 if err != nil { | |
205 log.Println("ERROR:", err) | |
206 } | |
207 lastModified = time.Now() | |
208 if once { | |
209 break | |
210 } | |
211 time.Sleep(1 * time.Second) | |
212 } | |
213 } | |
214 | |
215 func main() { | |
216 if len(os.Args) == 1 { | |
217 fmt.Println(os.Args[0], "<command> [args]") | |
218 return | |
219 } | |
220 cmd := os.Args[1] | |
221 args := os.Args[2:] | |
222 switch cmd { | |
223 case "build": | |
224 buildAll(true) | |
225 case "watch": | |
226 buildAll(false) // pass duration | |
227 case "var": | |
228 if len(args) == 0 { | |
229 // print error | |
230 return | |
231 } | |
232 if b, err := ioutil.ReadFile(args[0]); err == nil { | |
233 vars, _ := md(string(b)) | |
234 defaultVars(vars, args[0]) | |
235 if len(args) > 1 { | |
236 for _, a := range args[1:] { | |
237 fmt.Println(vars[a]) | |
238 } | |
239 } else { | |
240 for k, v := range vars { | |
241 fmt.Println(k + ":" + v) | |
242 } | |
243 } | |
244 } else { | |
245 // print error | |
246 } | |
247 default: | |
248 cmd := exec.Command(path.Join(ZSDIR, cmd), args...) | |
249 cmd.Env = append(cmd.Env, "ZS="+os.Args[0]) | |
250 cmd.Stdout = os.Stdout | |
251 cmd.Stderr = os.Stderr | |
252 if err := cmd.Run(); err != nil { | |
253 log.Println(err) | |
254 } | |
255 } | |
256 } |