| package packp |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io" |
| "time" |
| |
| "gopkg.in/src-d/go-git.v4/plumbing" |
| "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" |
| ) |
| |
| // Encode writes the UlReq encoding of u 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 (u *UploadRequest) Encode(w io.Writer) error { |
| e := newUlReqEncoder(w) |
| return e.Encode(u) |
| } |
| |
| type ulReqEncoder struct { |
| pe *pktline.Encoder // where to write the encoded data |
| data *UploadRequest // the data to encode |
| err error // sticky error |
| } |
| |
| func newUlReqEncoder(w io.Writer) *ulReqEncoder { |
| return &ulReqEncoder{ |
| pe: pktline.NewEncoder(w), |
| } |
| } |
| |
| func (e *ulReqEncoder) Encode(v *UploadRequest) error { |
| e.data = v |
| |
| if len(v.Wants) == 0 { |
| return fmt.Errorf("empty wants provided") |
| } |
| |
| plumbing.HashesSort(e.data.Wants) |
| for state := e.encodeFirstWant; state != nil; { |
| state = state() |
| } |
| |
| return e.err |
| } |
| |
| func (e *ulReqEncoder) encodeFirstWant() stateFn { |
| var err error |
| if e.data.Capabilities.IsEmpty() { |
| err = e.pe.Encodef("want %s\n", e.data.Wants[0]) |
| } else { |
| err = e.pe.Encodef( |
| "want %s %s\n", |
| e.data.Wants[0], |
| e.data.Capabilities.String(), |
| ) |
| } |
| |
| if err != nil { |
| e.err = fmt.Errorf("encoding first want line: %s", err) |
| return nil |
| } |
| |
| return e.encodeAditionalWants |
| } |
| |
| func (e *ulReqEncoder) encodeAditionalWants() stateFn { |
| last := e.data.Wants[0] |
| for _, w := range e.data.Wants[1:] { |
| if bytes.Equal(last[:], w[:]) { |
| continue |
| } |
| |
| if err := e.pe.Encodef("want %s\n", w); err != nil { |
| e.err = fmt.Errorf("encoding want %q: %s", w, err) |
| return nil |
| } |
| |
| last = w |
| } |
| |
| return e.encodeShallows |
| } |
| |
| func (e *ulReqEncoder) encodeShallows() stateFn { |
| plumbing.HashesSort(e.data.Shallows) |
| |
| var last plumbing.Hash |
| for _, s := range e.data.Shallows { |
| if bytes.Equal(last[:], s[:]) { |
| continue |
| } |
| |
| if err := e.pe.Encodef("shallow %s\n", s); err != nil { |
| e.err = fmt.Errorf("encoding shallow %q: %s", s, err) |
| return nil |
| } |
| |
| last = s |
| } |
| |
| return e.encodeDepth |
| } |
| |
| func (e *ulReqEncoder) encodeDepth() stateFn { |
| 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 e.encodeFlush |
| } |
| |
| func (e *ulReqEncoder) encodeFlush() stateFn { |
| if err := e.pe.Flush(); err != nil { |
| e.err = fmt.Errorf("encoding flush-pkt: %s", err) |
| return nil |
| } |
| |
| return nil |
| } |