package firstview import ( "context" "strconv" "strings" "time" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-sdk/go/approval" "git.novox.be/novox/mesh-sdk/go/seatdef" runnerapproval "github.com/novox/mesh-tools/node-tools/internal/approval" ) // Findings is the runner's held finding writer, the one the gate writes its findings through too (design 46 7-approval-mark // §4, §16, §19); nil when it was not made. func (v *Views) Findings() runnerapproval.HeldWriter { if v.deps.Findings == nil { return nil } return v.deps.Findings } // ApprovalCatalog is the catalog half as the gate reads its marks (design 46 7-approval-mark §5): the entries the branch // follower holds, an entry by fingerprint asked of `mesh-catalog.entry` when no branch lists it, and the seat definitions // the seat list holds (defs). func (v *Views) ApprovalCatalog(defs func() map[string]seatdef.Definition) approval.Catalog { return approvalCatalog{v: v, defs: defs} } type approvalCatalog struct { v *Views defs func() map[string]seatdef.Definition } func (c approvalCatalog) Ready() error { if c.v.branches == nil { return errNotFollowed } return c.v.branches.Ready() } func (c approvalCatalog) Revision() string { if c.v.branches == nil { return "" } for _, branch := range c.v.branches.Followed() { if cat, ok := c.v.branches.Catalog(branch); ok { if half, ok := cat.View(); ok { return strconv.FormatInt(half.Revision.Revision, 10) } } } return "" } func (c approvalCatalog) Served(fingerprint string) (approval.Entry, bool, string) { if c.v.branches == nil { return approval.Entry{}, false, errNotFollowed.Error() } e, state := c.v.branches.Resolve(context.Background(), fingerprint) return runnerapproval.Entry(e), state == meshcatalogclient.EntryAnswered, string(state) } func (c approvalCatalog) Newest(module string) (approval.Entry, bool) { if c.v.branches == nil { return approval.Entry{}, false } e, ok, err := c.v.branches.Entry(module) return runnerapproval.Entry(e), ok && err == nil } func (c approvalCatalog) Definitions() map[string]seatdef.Definition { if c.defs == nil { return nil } return c.defs() } func (c approvalCatalog) Entries() []approval.Entry { halves, err := c.v.Halves() if err != nil { return nil } var out []approval.Entry for _, half := range halves { for _, e := range half.Entries { out = append(out, runnerapproval.Entry(e)) } } return out } type notFollowed struct{} func (notFollowed) Error() string { return "the catalog half is not followed by this runner" } var errNotFollowed error = notFollowed{} // heardOne wakes every wait on the picture. func (v *Views) heardOne() { v.mu.Lock() defer v.mu.Unlock() if v.heard != nil { close(v.heard) } v.heard = make(chan struct{}) } // heardNext is closed at the next event heard. func (v *Views) heardNext() <-chan struct{} { v.mu.Lock() defer v.mu.Unlock() if v.heard == nil { v.heard = make(chan struct{}) } return v.heard } // WaitCatalog waits within the bound for the catalog half's first view of every watched branch (design 46 7-approval-mark // §19), waking at each event the runner hears and at each first view answered; nil once it is read, else why not. func (v *Views) WaitCatalog(within time.Duration) error { c := approvalCatalog{v: v} timer := time.NewTimer(within) defer timer.Stop() for { next := v.heardNext() err := c.Ready() if err == nil { return nil } var ready []<-chan struct{} if v.branches != nil { for _, branch := range v.branches.Followed() { if cat, ok := v.branches.Catalog(branch); ok { select { case <-cat.Ready(): // read already: waits on nothing default: ready = append(ready, cat.Ready()) } } } } if !waitAny(next, timer.C, ready) { return c.Ready() } } } // waitAny waits for next or one of ready, true, or for the bound, false. func waitAny(next <-chan struct{}, bound <-chan time.Time, ready []<-chan struct{}) bool { woke := make(chan struct{}, 1) stop := make(chan struct{}) defer close(stop) for _, r := range ready { go func(r <-chan struct{}) { select { case <-r: woke <- struct{}{} case <-stop: } }(r) } select { case <-next: case <-woke: case <-bound: return false } return true } // OnSeatHolder calls f whenever the assigner's seat-holders view changes for the seat (lead answer A-157: the gate asks // mesh-hand-acts.warranted again when the hand-act seat's holder is heard again). func (v *Views) OnSeatHolder(seat string, f func()) { v.mu.Lock() defer v.mu.Unlock() if v.onKey == nil { v.onKey = map[string][]func(){} } k := "seat-holders " + seat v.onKey[k] = append(v.onKey[k], f) } // heardKey calls the hearers of a key of an owner's state just changed: a seat's holders, under the seat's own key and // its `.` keys. func (v *Views) heardKey(state, key string) { seat, _, _ := strings.Cut(key, ".") v.mu.Lock() fs := append([]func(){}, v.onKey[state+" "+seat]...) v.mu.Unlock() for _, f := range fs { go f() } }