Nav apraksta

blog.go 4.7KB

    package main import ( "crypto/md5" "crypto/rand" "encoding/hex" "flag" "fmt" "html/template" "io" "io/ioutil" "os" "github.com/russross/blackfriday" "gopkg.in/yaml.v2" ) type Post struct { Id string `yaml:"id"` Title string `yaml:"title"` URL string `yaml:"url"` Content string `yaml:"content"` Date string `yaml:"date"` Type string `yaml:"type"` } var flags struct { writeBack bool reverse bool css string title string } var dataPath string = "blog.yaml" var defaultStyle = ` article { margin-bottom: 1em; } article header { display: flex; align-items: center; } article h1 { margin: 0; margin-right: 1em; } article time { color: #666; } article img { max-width: 80vw; max-height: 50vh; } ` func init() { flag.BoolVar(&flags.writeBack, "write-back", false, "Rewrite the YAML file with the generated ids") flag.BoolVar(&flags.reverse, "reverse", false, "Reverse the order of the articles in the file") flag.StringVar(&flags.css, "css", defaultStyle, "Custom styles to use") flag.StringVar(&flags.title, "title", "A blog", "Custom title to use") } func main() { flag.Parse() if flag.NArg() > 0 { dataPath = flag.Arg(0) } f, err := os.Open(dataPath) if err != nil { exit(err) } defer f.Close() data, err := ioutil.ReadAll(f) if err != nil { exit(err) } var posts []Post err = yaml.Unmarshal(data, &posts) if err != nil { exit(err) } fmt.Printf(`<!doctype html> <html> <head> <meta charset="utf-8" /> <title>%s</title> <style>%s</style> </head> <body> `, flags.title, flags.css) if flags.reverse { l := len(posts) reversePosts := make([]Post, l) for i := 0; i < l; i++ { reversePosts[i] = posts[l-i-1] } posts = reversePosts } for i, post := range posts { if post.Id == "" { posts[i].Id = generateId(post) post = posts[i] } var err error switch post.Type { case "shell": err = shellTmpl.Execute(os.Stdout, post) case "link": err = linkTmpl.Execute(os.Stdout, post) case "image": err = imageTmpl.Execute(os.Stdout, post) case "song": err = songTmpl.Execute(os.Stdout, post) case "text": err = textTmpl.Execute(os.Stdout, post) default: fmt.Fprintf(os.Stderr, "Error: no output for type '%s'\n", post.Type) os.Exit(1) } if err != nil { exit(err) } } fmt.Printf("\n</body>\n</html>\n") if flags.writeBack { out, err := yaml.Marshal(posts) if err != nil { exit(err) } ioutil.WriteFile(dataPath, out, 0664) } } var funcs = template.FuncMap{ "markdown": func(markdown string) template.HTML { return template.HTML(blackfriday.MarkdownCommon([]byte(markdown))) }, "safe_url": func(s string) template.URL { return template.URL(s) }, } var shellTmpl = template.Must(template.New("shell"). Funcs(funcs).Parse(` <article id="{{ .Id }}" class="shell"> <header> <h1><code class="language-shell">{{ .Title }}</code></h1> {{- if .Date }}<time>{{ .Date }}</time>{{ end -}} </header> {{- if .Content }} {{ markdown .Content }} {{- end -}} </article> `)) var linkTmpl = template.Must(template.New("link"). Funcs(funcs).Parse(` <article id="{{ .Id }}" class="link"> <header> <h1><a href="{{ .URL }}">{{ .Title }}</a></h1> {{- if .Date }}<time>{{ .Date }}</time>{{ end -}} </header> {{- if .Content }} {{ markdown .Content }} {{- end -}} </article> `)) var imageTmpl = template.Must(template.New("image"). Funcs(funcs).Parse(` <article id="{{ .Id }}" class="image"> {{- if .Title }} <header> <h1>{{ .Title }}</h1> {{- if .Date }}<time>{{ .Date }}</time>{{ end -}} </header> {{- end }} <img src="{{ safe_url .URL }}" /> {{- if .Content }} {{ markdown .Content }} {{- end -}} </article> `)) var songTmpl = template.Must(template.New("song"). Funcs(funcs).Parse(` <article id="{{ .Id }}" class="song"> {{- if .Title }} <header> <h1>{{ .Title }}</h1> {{- if .Date }}<time>{{ .Date }}</time>{{ end -}} </header> {{- end }} <audio src="{{ safe_url .URL }}" controls> Your browser can't play {{ .URL }}. </audio> {{- if .Content }} {{ markdown .Content }} {{- end -}} </article> `)) var textTmpl = template.Must(template.New("text"). Funcs(funcs).Parse(` <article id="{{ .Id }}" class="text"> <header> <h1>{{ .Title }}</h1> {{- if .Date }}<time>{{ .Date }}</time>{{ end -}} </header> {{- if .Content }} {{ markdown .Content }} {{- end -}} </article> `)) func exit(err error) { fmt.Fprintf(os.Stderr, "Error: %s\n", err) os.Exit(1) } func generateId(p Post) string { h := md5.New() io.WriteString(h, p.Title) io.WriteString(h, p.Content) io.WriteString(h, p.Type) return hex.EncodeToString(h.Sum(nil)) } func randomId() string { buf := make([]byte, 16) _, err := rand.Read(buf) if err != nil { panic(err) } return hex.EncodeToString(buf) }