func (c *Collection) Slice() []*Revision {
	revs := make([]*Revision, 0, c.len)
	c.root.walk(func(r *Revision) {
		revs = append(revs, r)
	})
	return revs
}
Slice returns a sorted slice of all the revisions in the collection.
package mgrt
import (
	"bufio"
	"bytes"
	"database/sql"
	"errors"
	"io"
	"os"
	"strings"
	"time"
)
type node struct {
	val   int64
	rev   *Revision
	left  *node
	right *node
}
type Errors []error
type Revision struct {
	ID          string     
	Category    string     
	Author      string     
	Comment     string     
	SQL         string     
	PerformedAt time.Time  
}
type RevisionError struct {
	ID  string  
	Err error   
}
type Collection struct {
	len  int
	root *node
}
var (
	revisionIdFormat = "20060102150405"
	ErrInvalid = errors.New("revision id invalid")
	ErrPerformed = errors.New("revision performed")
	ErrNotFound = errors.New("revision not found")
)
func insertNode(n **node, val int64, r *Revision) {
	if (*n) == nil {
		(*n) = &node{
			val: val,
			rev: r,
		}
		return
	}
	if val < (*n).val {
		insertNode(&(*n).left, val, r)
		return
	}
	insertNode(&(*n).right, val, r)
}
func NewRevision(author, comment string) *Revision {
	return &Revision{
		ID:      time.Now().Format(revisionIdFormat),
		Author:  author,
		Comment: comment,
	}
}
func NewRevisionCategory(category, author, comment string) *Revision {
	rev := NewRevision(author, comment)
	rev.Category = category
	return rev
}
func RevisionPerformed(db *DB, rev *Revision) error {
	var count int64
	if _, err := time.Parse(revisionIdFormat, rev.ID); err != nil {
		return ErrInvalid
	}
	q := db.Parameterize("SELECT COUNT(id) FROM mgrt_revisions WHERE (id = ?)")
	if err := db.QueryRow(q, rev.Slug()).Scan(&count); err != nil {
		return &RevisionError{
			ID:  rev.Slug(),
			Err: err,
		}
	}
	if count > 0 {
		return &RevisionError{
			ID:  rev.Slug(),
			Err: ErrPerformed,
		}
	}
	return nil
}
func GetRevision(db *DB, id string) (*Revision, error) {
	var (
		rev Revision
		sec int64
	)
	q := "SELECT id, author, comment, sql, performed_at FROM mgrt_revisions WHERE (id = ?)"
	row := db.QueryRow(db.Parameterize(q), id)
	var categoryid string
	if err := row.Scan(&categoryid, &rev.Author, &rev.Comment, &rev.SQL, &sec); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, &RevisionError{
				ID:  categoryid,
				Err: ErrNotFound,
			}
		}
		return nil, err
	}
	parts := strings.Split(categoryid, "/")
	end := len(parts) - 1
	rev.ID = parts[end]
	rev.Category = strings.Join(parts[:end], "/")
	rev.PerformedAt = time.Unix(sec, 0)
	return &rev, nil
}
func GetRevisions(db *DB, n int) ([]*Revision, error) {
	count := int64(n)
	if n <= 0 {
		q0 := "SELECT COUNT(id) FROM mgrt_revisions"
		if err := db.QueryRow(q0).Scan(&count); err != nil {
			return nil, err
		}
	}
	revs := make([]*Revision, 0, int(count))
	q := "SELECT id, author, comment, sql, performed_at FROM mgrt_revisions ORDER BY id DESC LIMIT ?"
	rows, err := db.Query(db.Parameterize(q), count)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var (
			rev        Revision
			sec        int64
			categoryid string
		)
		err = rows.Scan(&categoryid, &rev.Author, &rev.Comment, &rev.SQL, &sec)
		if err != nil {
			return nil, err
		}
		parts := strings.Split(categoryid, "/")
		end := len(parts) - 1
		rev.ID = parts[end]
		rev.Category = strings.Join(parts[:end], "/")
		rev.PerformedAt = time.Unix(sec, 0)
		revs = append(revs, &rev)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return revs, nil
}
func PerformRevisions(db *DB, revs0 ...*Revision) error {
	var c Collection
	for _, rev := range revs0 {
		c.Put(rev)
	}
	errs := Errors(make([]error, 0, len(revs0)))
	revs := c.Slice()
	for _, rev := range revs {
		if err := rev.Perform(db); err != nil {
			if errors.Is(err, ErrPerformed) {
				errs = append(errs, err)
				continue
			}
			return err
		}
	}
	return errs.err()
}
func OpenRevision(path string) (*Revision, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return UnmarshalRevision(f)
}
func UnmarshalRevision(r io.Reader) (*Revision, error) {
	br := bufio.NewReader(r)
	rev := &Revision{}
	var (
		buf     []rune = make([]rune, 0)
		r0      rune
		inBlock bool
	)
	for {
		r, _, err := br.ReadRune()
		if err != nil {
			if err != io.EOF {
				return nil, err
			}
			rev.SQL = strings.TrimSpace(string(buf))
			break
		}
		if r == '*' {
			if r0 == '/' {
				inBlock = true
				continue
			}
		}
		if r == '/' {
			if r0 == '*' {
				rev.Comment = strings.TrimSpace(string(buf))
				buf = buf[0:0]
				inBlock = false
				continue
			}
		}
		if inBlock {
			if r == '\n' {
				if r0 == '\n' {
					if rev.ID == "" && rev.Author == "" {
						buf = buf[0:0]
					}
					goto cont
				}
				pos := -1
				for i, r := range buf {
					if r == ':' {
						pos = i
						break
					}
				}
				if pos < 0 {
					goto cont
				}
				if string(buf[pos-6:pos]) == "Author" {
					rev.Author = strings.TrimSpace(string(buf[pos+1:]))
					buf = buf[0:0]
					continue
				}
				if string(buf[pos-8:pos]) == "Revision" {
					rev.ID = strings.TrimSpace(string(buf[pos+1:]))
					buf = buf[0:0]
					continue
				}
			}
		}
		if r == '*' {
			peek, _, err := br.ReadRune()
			if err != nil {
				if err != io.EOF {
					return nil, err
				}
				continue
			}
			if peek == '/' {
				br.UnreadRune()
				r0 = r
				continue
			}
		}
	cont:
		buf = append(buf, r)
		r0 = r
	}
	parts := strings.Split(rev.ID, "/")
	end := len(parts)-1
	rev.ID = parts[len(parts)-1]
	rev.Category = strings.Join(parts[:end], "/")
	if _, err := time.Parse(revisionIdFormat, rev.ID); err != nil {
		return nil, ErrInvalid
	}
	return rev, nil
}
func (n *node) walk(visit func(*Revision)) {
	if n.left != nil {
		n.left.walk(visit)
	}
	visit(n.rev)
	if n.right != nil {
		n.right.walk(visit)
	}
}
func (e Errors) err() error {
	if len(e) == 0 {
		return nil
	}
	return e
}
func (e Errors) Error() string {
	var buf bytes.Buffer
	for _, err := range e {
		buf.WriteString(err.Error() + "\n")
	}
	return buf.String()
}
func (c *Collection) Put(r *Revision) error {
	if r.ID == "" {
		return ErrInvalid
	}
	t, err := time.Parse(revisionIdFormat, r.ID)
	if err != nil {
		return ErrInvalid
	}
	insertNode(&c.root, t.Unix(), r)
	c.len++
	return nil
}
func (c *Collection) Len() int { return c.len }