package packp import ( "bytes" "io" "sort" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability" ) // Encode writes the AdvRefs encoding to a writer. // // All the payloads will end with a newline character. Capabilities, // references and shallows are written in alphabetical order, except for // peeled references that always follow their corresponding references. func (a *AdvRefs) Encode(w io.Writer) error { e := newAdvRefsEncoder(w) return e.Encode(a) } type advRefsEncoder struct { data *AdvRefs // data to encode pe *pktline.Encoder // where to write the encoded data err error // sticky error } func newAdvRefsEncoder(w io.Writer) *advRefsEncoder { return &advRefsEncoder{ pe: pktline.NewEncoder(w), } } func (e *advRefsEncoder) Encode(v *AdvRefs) error { e.data = v for state := encodePrefix; state != nil; { state = state(e) } return e.err } type encoderStateFn func(*advRefsEncoder) encoderStateFn func encodePrefix(e *advRefsEncoder) 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 *advRefsEncoder) 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 *plumbing.Hash) string { if h == nil { return plumbing.ZeroHash.String() } return h.String() } func formatSeparator(h *plumbing.Hash) string { if h == nil { return noHead } return head } func formatCaps(c *capability.List) string { if c == nil { return "" } return c.String() } // Adds the (sorted) refs: hash SP refname EOL // and their peeled refs if any. func encodeRefs(e *advRefsEncoder) 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]plumbing.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 *advRefsEncoder) 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 []plumbing.Hash) []string { ret := []string{} for _, h := range c { ret = append(ret, h.String()) } sort.Strings(ret) return ret } func encodeFlush(e *advRefsEncoder) encoderStateFn { e.err = e.pe.Flush() return nil }