func (p *PostIndex) Print() {
	fmt.Println(strings.Repeat("#", p.Level), p)
	for _, c := range p.Children {
		c.Print()
	}
}
Print prints post indexs friendly
package model
import (
	"bytes"
	"errors"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"path/filepath"
	"strings"
	"time"
	"github.com/BurntSushi/toml"
	"github.com/Unknwon/com"
	"github.com/go-xiaohei/pugo/app/helper"
	"golang.org/x/net/html"
	"gopkg.in/ini.v1"
)
var (
	tomlPrefix         = []byte("toml")
	titleReplacer      = strings.NewReplacer(" ", "-")
	postBlockSeparator = []byte("```")
	postBriefSeparator = []byte("<!--more-->")
)
type Post struct {
	Title      string       `toml:"title" ini:"title"`
	Slug       string       `toml:"slug" ini:"slug"`
	Desc       string       `toml:"desc" ini:"desc"`
	Date       string       `toml:"date" ini:"date"`
	Update     string       `toml:"update_date" ini:"update_date"`
	AuthorName string       `toml:"author" ini:"author"`
	Thumb      string       `toml:"thumb" ini:"thumb"`
	Draft      bool         `toml:"draft" ini:"draft"`
	TagString  []string     `toml:"tags" ini:"-"`
	Tags       []*Tag       `toml:"-" ini:"-"`
	Author     *Author      `toml:"-" ini:"-"`
	Index      []*PostIndex `toml:"-" ini:"-"`
	dateTime   time.Time
	updateTime time.Time
	Bytes        []byte `toml:"-"`
	contentBytes []byte
	briefBytes   []byte
	postURL      string
	fileURL      string
	destURL      string
}
func (p *Post) SetURL(url string) {
	p.postURL = url
}
func (p *Post) SetDestURL(url string) {
	p.destURL = url
}
func (p *Post) SetPlaceholder(stringReplacer, htmlReplacer *strings.Replacer) {
	p.Thumb = stringReplacer.Replace(p.Thumb)
	p.contentBytes = []byte(htmlReplacer.Replace(string(p.contentBytes)))
	p.briefBytes = []byte(htmlReplacer.Replace(string(p.briefBytes)))
}
func (p *Post) URL() string {
	return p.postURL
}
func (p *Post) SourceURL() string {
	return filepath.ToSlash(p.fileURL)
}
func (p *Post) DestURL() string {
	return filepath.ToSlash(p.destURL)
}
func (p *Post) ContentHTML() template.HTML {
	return template.HTML(p.contentBytes)
}
func (p *Post) Content() []byte {
	return p.contentBytes
}
func (p *Post) BriefHTML() template.HTML {
	return template.HTML(p.briefBytes)
}
func (p *Post) Brief() []byte {
	return p.briefBytes
}
func (p *Post) PreviewHTML() template.HTML {
	return p.BriefHTML()
}
func (p *Post) Preview() []byte {
	return p.Brief()
}
func (p *Post) Created() time.Time {
	return p.dateTime
}
func (p *Post) Updated() time.Time {
	return p.updateTime
}
func (p *Post) IsUpdated() bool {
	return p.updateTime.Unix() != p.dateTime.Unix()
}
func (p *Post) normalize() error {
	if p.Slug == "" {
		p.Slug = strings.TrimSuffix(filepath.Base(p.fileURL), filepath.Ext(p.fileURL))
	}
	var err error
	if p.Date != "" {
		if p.dateTime, err = parseTimeString(p.Date); err != nil {
			return err
		}
	}
	if p.Update == "" {
		p.Update = p.Date
		p.updateTime = p.dateTime
	} else {
		if p.updateTime, err = parseTimeString(p.Update); err != nil {
			return err
		}
	}
	p.contentBytes = helper.Markdown(p.Bytes)
	p.briefBytes = helper.Markdown(bytes.Split(p.Bytes, postBriefSeparator)[0])
	permaURL := fmt.Sprintf("/%d/%d/%d/%s", p.dateTime.Year(), p.dateTime.Month(), p.dateTime.Day(), p.Slug)
	p.postURL = permaURL + ".html"
	for _, t := range p.TagString {
		p.Tags = append(p.Tags, NewTag(t))
	}
	p.Index = newPostIndexs(bytes.NewReader(p.contentBytes))
	return nil
}
func NewPostOfMarkdown(file string, post *Post) (*Post, error) {
	fileBytes, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	if len(fileBytes) < 3 {
		return nil, fmt.Errorf("post content is too less")
	}
	if post == nil {
		dataSlice := bytes.SplitN(fileBytes, postBlockSeparator, 3)
		if len(dataSlice) != 3 {
			return nil, fmt.Errorf("post need front-matter block and markdown block")
		}
		idx := getFirstBreakByte(dataSlice[1])
		if idx == 0 {
			return nil, fmt.Errorf("post need front-matter block and markdown block")
		}
		formatType := detectFormat(string(dataSlice[1][:idx]))
		if formatType == 0 {
			return nil, fmt.Errorf("post front-matter block is unrecognized")
		}
		post = new(Post)
		if formatType == FormatTOML {
			if err = toml.Unmarshal(dataSlice[1][idx:], post); err != nil {
				return nil, err
			}
		}
		if formatType == FormatINI {
			iniObj, err := ini.Load(dataSlice[1][idx:])
			if err != nil {
				return nil, err
			}
			section := iniObj.Section("DEFAULT")
			if err = newPostFromIniSection(section, post); err != nil {
				return nil, err
			}
		}
		post.Bytes = bytes.Trim(dataSlice[2], "\n")
	} else {
		post.Bytes = bytes.Trim(fileBytes, "\n")
	}
	post.fileURL = file
	if post.Date == "" {
		t, _ := com.FileMTime(file)
		post.dateTime = time.Unix(t, 0)
	}
	return post, post.normalize()
}
func parseTimeString(timeStr string) (time.Time, error) {
	timeStr = strings.TrimSpace(timeStr)
	if len(timeStr) == 0 {
		return time.Time{}, errors.New("empty time string")
	}
	if len(timeStr) == len("2006-01-02") {
		return time.Parse("2006-01-02", timeStr)
	}
	if len(timeStr) == len("2006-01-02 15:04") {
		return time.Parse("2006-01-02 15:04", timeStr)
	}
	if len(timeStr) == len("2006-01-02 15:04:05") {
		return time.Parse("2006-01-02 15:04:05", timeStr)
	}
	return time.Time{}, errors.New("unknown time string")
}
func getFirstBreakByte(data []byte) int {
	for i, v := range data {
		if v == 10 {
			return i
		}
	}
	return 0
}
type PostIndex struct {
	Level    int
	Title    string
	Archor   string
	Children []*PostIndex
	Link     string
	Parent   *PostIndex
}