diff options
Diffstat (limited to 'formats/packp/ulreq/encoder.go')
-rw-r--r-- | formats/packp/ulreq/encoder.go | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/formats/packp/ulreq/encoder.go b/formats/packp/ulreq/encoder.go new file mode 100644 index 0000000..1e40b63 --- /dev/null +++ b/formats/packp/ulreq/encoder.go @@ -0,0 +1,140 @@ +package ulreq + +import ( + "fmt" + "io" + "sort" + "time" + + "gopkg.in/src-d/go-git.v4/core" + "gopkg.in/src-d/go-git.v4/formats/packp/pktline" +) + +// An Encoder writes UlReq values to an output stream. +type Encoder struct { + pe *pktline.Encoder // where to write the encoded data + data *UlReq // the data to encode + sortedWants []string + 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 UlReq encoding of v to the stream. +// +// All the payloads will end with a newline character. Wants and +// shallows are sorted alphabetically. A depth of 0 means no depth +// request is sent. +func (e *Encoder) Encode(v *UlReq) error { + if len(v.Wants) == 0 { + return fmt.Errorf("empty wants provided") + } + + e.data = v + e.sortedWants = sortHashes(v.Wants) + + for state := encodeFirstWant; state != nil; { + state = state(e) + } + + return e.err +} + +type encoderStateFn func(*Encoder) encoderStateFn + +func sortHashes(list []core.Hash) []string { + sorted := make([]string, len(list)) + for i, hash := range list { + sorted[i] = hash.String() + } + sort.Strings(sorted) + + return sorted +} + +func encodeFirstWant(e *Encoder) encoderStateFn { + var err error + if e.data.Capabilities.IsEmpty() { + err = e.pe.Encodef("want %s\n", e.sortedWants[0]) + } else { + e.data.Capabilities.Sort() + err = e.pe.Encodef( + "want %s %s\n", + e.sortedWants[0], + e.data.Capabilities.String(), + ) + } + if err != nil { + e.err = fmt.Errorf("encoding first want line: %s", err) + return nil + } + + return encodeAditionalWants +} + +func encodeAditionalWants(e *Encoder) encoderStateFn { + for _, w := range e.sortedWants[1:] { + if err := e.pe.Encodef("want %s\n", w); err != nil { + e.err = fmt.Errorf("encoding want %q: %s", w, err) + return nil + } + } + + return encodeShallows +} + +func encodeShallows(e *Encoder) encoderStateFn { + sorted := sortHashes(e.data.Shallows) + for _, s := range sorted { + if err := e.pe.Encodef("shallow %s\n", s); err != nil { + e.err = fmt.Errorf("encoding shallow %q: %s", s, err) + return nil + } + } + + return encodeDepth +} + +func encodeDepth(e *Encoder) encoderStateFn { + switch depth := e.data.Depth.(type) { + case DepthCommits: + if depth != 0 { + commits := int(depth) + if err := e.pe.Encodef("deepen %d\n", commits); err != nil { + e.err = fmt.Errorf("encoding depth %d: %s", depth, err) + return nil + } + } + case DepthSince: + when := time.Time(depth).UTC() + if err := e.pe.Encodef("deepen-since %d\n", when.Unix()); err != nil { + e.err = fmt.Errorf("encoding depth %s: %s", when, err) + return nil + } + case DepthReference: + reference := string(depth) + if err := e.pe.Encodef("deepen-not %s\n", reference); err != nil { + e.err = fmt.Errorf("encoding depth %s: %s", reference, err) + return nil + } + default: + e.err = fmt.Errorf("unsupported depth type") + return nil + } + + return encodeFlush +} + +func encodeFlush(e *Encoder) encoderStateFn { + if err := e.pe.Flush(); err != nil { + e.err = fmt.Errorf("encoding flush-pkt: %s", err) + return nil + } + + return nil +} |