aboutsummaryrefslogblamecommitdiffstats
path: root/formats/packp/advrefs/encoder.go
blob: 98748846788589e9b57561277996885e5e5124ac (plain) (tree)


























































































































































                                                                                               
package advrefs

import (
	"bytes"
	"io"
	"sort"

	"gopkg.in/src-d/go-git.v4/core"
	"gopkg.in/src-d/go-git.v4/formats/packp"
	"gopkg.in/src-d/go-git.v4/formats/packp/pktline"
)

// An Encoder writes AdvRefs values to an output stream.
type Encoder struct {
	data *AdvRefs         // data to encode
	pe   *pktline.Encoder // where to write the encoded data
	err  error            // sticky error
}

// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{
		pe: pktline.NewEncoder(w),
	}
}

// Encode writes the AdvRefs encoding of v to the stream.
//
// All the payloads will end with a newline character.  Capabilities,
// references and shallows are writen in alphabetical order, except for
// peeled references that always follow their corresponding references.
func (e *Encoder) Encode(v *AdvRefs) error {
	e.data = v

	for state := encodePrefix; state != nil; {
		state = state(e)
	}

	return e.err
}

type encoderStateFn func(*Encoder) encoderStateFn

func encodePrefix(e *Encoder) encoderStateFn {
	for _, p := range e.data.Prefix {
		if bytes.Equal(p, pktline.Flush) {
			if e.err = e.pe.Flush(); e.err != nil {
				return nil
			}
			continue
		}
		if e.err = e.pe.Encodef("%s\n", string(p)); e.err != nil {
			return nil
		}
	}

	return encodeFirstLine
}

// Adds the first pkt-line payload: head hash, head ref and capabilities.
// Also handle the special case when no HEAD ref is found.
func encodeFirstLine(e *Encoder) encoderStateFn {
	head := formatHead(e.data.Head)
	separator := formatSeparator(e.data.Head)
	capabilities := formatCaps(e.data.Capabilities)

	if e.err = e.pe.Encodef("%s %s\x00%s\n", head, separator, capabilities); e.err != nil {
		return nil
	}

	return encodeRefs
}

func formatHead(h *core.Hash) string {
	if h == nil {
		return core.ZeroHash.String()
	}

	return h.String()
}

func formatSeparator(h *core.Hash) string {
	if h == nil {
		return noHead
	}

	return head
}

func formatCaps(c *packp.Capabilities) string {
	if c == nil {
		return ""
	}

	c.Sort()

	return c.String()
}

// Adds the (sorted) refs: hash SP refname EOL
// and their peeled refs if any.
func encodeRefs(e *Encoder) encoderStateFn {
	refs := sortRefs(e.data.References)
	for _, r := range refs {
		hash, _ := e.data.References[r]
		if e.err = e.pe.Encodef("%s %s\n", hash.String(), r); e.err != nil {
			return nil
		}

		if hash, ok := e.data.Peeled[r]; ok {
			if e.err = e.pe.Encodef("%s %s^{}\n", hash.String(), r); e.err != nil {
				return nil
			}
		}
	}

	return encodeShallow
}

func sortRefs(m map[string]core.Hash) []string {
	ret := make([]string, 0, len(m))
	for k := range m {
		ret = append(ret, k)
	}
	sort.Strings(ret)

	return ret
}

// Adds the (sorted) shallows: "shallow" SP hash EOL
func encodeShallow(e *Encoder) encoderStateFn {
	sorted := sortShallows(e.data.Shallows)
	for _, hash := range sorted {
		if e.err = e.pe.Encodef("shallow %s\n", hash); e.err != nil {
			return nil
		}
	}

	return encodeFlush
}

func sortShallows(c []core.Hash) []string {
	ret := []string{}
	for _, h := range c {
		ret = append(ret, h.String())
	}
	sort.Strings(ret)

	return ret
}

func encodeFlush(e *Encoder) encoderStateFn {
	e.err = e.pe.Flush()
	return nil
}