mirror of
https://github.com/samuelncui/acp.git
synced 2026-11-08 12:27:05 +00:00
Reshape the public API around one push engine and restore theaf05f05csurface as a compatibility shell over it. - NewStream takes caller-owned items (Source/Targets), reports every accepted item through one results callback, and Wait returns the run's terminal error. Run, BatchSource and the item callbacks are gone. - The engine owns the result path: WithResultBuffer is the depth of the result queue, WithResultBatch is how many results one delivery carries, and WithResultFlushInterval is how long a successful result may wait. A result that carries any error is delivered immediately as its own batch. - Restore theaf05f05cAPI byte-compatibly in the same package: New, Copyer, Wait/WaitErr, AccurateJob, WildcardJob, the report events and the JSON report round-trip through encoding/json alone. - One descriptor serves a whole item; the stored hash is read through the descriptor that reads the content and the computed hash is published through it before it closes. - Make every handoff escapable by a hard stop: the read buffer, a chunk send, an event send and the result buffer. - Fix the recorded sweep findings: report indent and round-trip, cmd/acp exit status, acp-rewrite final paths, mmap EOF/close/slice, disk-usage reservations, ErrTargetIO identity, the fatal-panic safety net, option validation, and the FreeBSD/Windows build paths. - Cover the contract with semantic tests, add Makefile check/race/vet/cross targets, and document the withdrawn v0.1.0/v0.2.0 migration in README.
88 lines
2.1 KiB
Go
88 lines
2.1 KiB
Go
package acp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
var (
|
|
_ = error(new(Error))
|
|
_ = json.Marshaler(new(Error))
|
|
_ = json.Unmarshaler(new(Error))
|
|
)
|
|
|
|
// Error is one pipeline-level failure. Its JSON form stores the message, so a report stays
|
|
// readable with encoding/json and an absent error stays absent.
|
|
type Error struct {
|
|
Src string `json:"src,omitempty"`
|
|
Dst string `json:"dst,omitempty"`
|
|
Err error `json:"error,omitempty"`
|
|
}
|
|
|
|
type jsonError struct {
|
|
Src string `json:"src,omitempty"`
|
|
Dst string `json:"dst,omitempty"`
|
|
Err string `json:"error,omitempty"`
|
|
}
|
|
|
|
// isNilError reports whether an error interface holds no error at all, including the typed
|
|
// nil a nil concrete pointer produces.
|
|
func isNilError(err error) bool {
|
|
if err == nil {
|
|
return true
|
|
}
|
|
|
|
value := reflect.ValueOf(err)
|
|
switch value.Kind() {
|
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
return value.IsNil()
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (e *Error) Error() string {
|
|
if e == nil {
|
|
return "<nil>"
|
|
}
|
|
if isNilError(e.Err) {
|
|
return fmt.Sprintf("[%s => %s]", e.Src, e.Dst)
|
|
}
|
|
return fmt.Sprintf("[%s => %s]: %s", e.Src, e.Dst, e.Err)
|
|
}
|
|
|
|
// MarshalJSON encodes the message of an error. A missing error stays missing: it is omitted
|
|
// instead of being rendered from a nil value, so marshalling cannot panic on the nil errors
|
|
// UnmarshalJSON can produce.
|
|
func (e *Error) MarshalJSON() ([]byte, error) {
|
|
if e == nil {
|
|
return []byte("null"), nil
|
|
}
|
|
|
|
message := ""
|
|
if !isNilError(e.Err) {
|
|
message = e.Err.Error()
|
|
}
|
|
return json.Marshal(&jsonError{Src: e.Src, Dst: e.Dst, Err: message})
|
|
}
|
|
|
|
// UnmarshalJSON decodes the object form. An absent or empty message leaves Err nil, which is
|
|
// what a report stores for it, so a round trip neither loses an error nor invents one.
|
|
func (e *Error) UnmarshalJSON(buf []byte) error {
|
|
if e == nil {
|
|
return errors.New("decode acp error failed, receiver is nil")
|
|
}
|
|
|
|
m := new(jsonError)
|
|
if err := json.Unmarshal(buf, &m); err != nil {
|
|
return err
|
|
}
|
|
|
|
e.Src, e.Dst, e.Err = m.Src, m.Dst, nil
|
|
if m.Err != "" {
|
|
e.Err = errors.New(m.Err)
|
|
}
|
|
return nil
|
|
}
|