Files
mesh-delivery/modules/mesh-delivery-teller/internal/ends/ends.go
T

82 lines
3.4 KiB
Go

// Package ends is the teller's recovery tool teller_end_ask and the value it writes (issue 774; the instruction "tools are
// how a module is spoken to"). An ask the teller holds open at the router keeps one of the three places the router gives
// its asker (asks.MostOpen) until it is answered, it lapses, or the gate or the assigner takes its key away. When that last
// never comes (on 2026-10-31 two merge asks of pull requests merged by another path held two places for a day, and a
// placement ask was refused for want of room), a person ends the ask by hand. The teller's process serves no request
// subject (ADR 0437 §4), so the tool writes an end into the teller's state ends, which the teller watches, carries out
// and answers there, as the indexer answers indexer_take_in.
package ends
import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient"
)
const (
// module is the teller's own name (its manifest's module), the holder of the bucket.
module = "mesh-delivery-teller"
Tool = "teller_end_ask"
// State is the teller's state of ends asked by hand: the bucket mesh-delivery-teller_ends.
State = "ends"
Bucket = module + "_" + State
)
// Result is the teller's answer to an end: which ask it found (its router id and the gate's or the assigner's key it was
// asked for), whether this end ended it, and what it did in words.
type Result struct {
ID string `json:"id,omitempty"`
Key string `json:"key,omitempty"`
Ended bool `json:"ended"`
Says string `json:"says"`
}
// End is a value of the state ends: the ask named (a router id, as operator-channel's asks show it, or the key of the
// gate's or the assigner's asks it was asked for), why, who and when, and, once the teller carried it out, its answer.
type End struct {
V int `json:"v"`
Ask string `json:"ask"`
Why string `json:"why"`
By string `json:"by,omitempty"`
At time.Time `json:"at"`
Result *Result `json:"result,omitempty"`
Answered time.Time `json:"answered,omitzero"`
}
// Done says the end is answered.
func (e End) Done() bool { return !e.Answered.IsZero() && e.Result != nil }
// Decode reads a value of the state ends.
func Decode(data []byte) (End, error) {
var e End
if err := json.Unmarshal(data, &e); err != nil {
return e, err
}
if e.V != 1 {
return e, fmt.Errorf("an ends value of version %d, not 1", e.V)
}
if e.Ask == "" || e.Why == "" {
return e, errors.New("an ends value naming no ask or no why")
}
return e, nil
}
// New is the value teller_end_ask writes: its key and the value. The key is the router's spelling of the name given
// (meshdeliveryclient.AskID), which a bucket key takes whatever the name holds, and is the same for the same name, so a second end
// of one ask overwrites the first.
func New(ask, why, by string, at time.Time) (string, []byte, error) {
ask, why = strings.TrimSpace(ask), strings.TrimSpace(why)
if ask == "" {
return "", nil, errors.New("name the ask to end (ask): its router id, as operator-channel's asks show it, or the key of the gate's or the assigner's asks")
}
if why == "" {
return "", nil, errors.New("say why the ask is ended (why), in plain words: it is logged with who ended it")
}
data, err := json.Marshal(End{V: 1, Ask: ask, Why: why, By: by, At: at.UTC()})
return meshdeliveryclient.AskID(ask), data, err
}