aboutsummaryrefslogtreecommitdiffstats
path: root/formats/packp/ulreq/encoder.go
diff options
context:
space:
mode:
Diffstat (limited to 'formats/packp/ulreq/encoder.go')
-rw-r--r--formats/packp/ulreq/encoder.go140
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
+}