package web
import "strings"
// stripState is the landing record's decision strip: one state, the
// label that names it, its tone, and the rung it is about when the
// state belongs to another rung of the stack. One function decides it,
// so the label, the sentence and the primary action cannot disagree.
type stripState struct {
// State is the strip's case, for the template and data-state:
// protected, stack-stops, stack-waits, stack-ready, stacked,
// conflict, landed, checks, stale, ready, checking, or "" for a
// record that is not open.
State string
Label string
// Tone is ok, warn, bad, or "" for a neutral label.
Tone string
// Rung is the stack rung the state is about, for stack-stops and
// stack-waits.
Rung rungRef
}
// rungRef is one rung of a stack as a sentence names it.
type rungRef struct {
ID, Title, Branch, Target, Preflight string
Number, Behind int
Conflicts, Waiting []string
Here bool
}
// Name is how a sentence names the rung: its number, else its branch.
func (r rungRef) Name() string {
if r.Number > 0 {
return "!" + itoa(r.Number)
}
return r.Branch
}
func itoa(n int) string {
if n == 0 {
return "0"
}
var b [20]byte
i := len(b)
for ; n > 0; n /= 10 {
i--
b[i] = byte('0' + n%10)
}
return string(b[i:])
}
// rungAt is the stack rung at position pos, counted from 1 at the base.
func (l landingDetail) rungAt(pos int) (rungRef, bool) {
for _, s := range l.Stack.Segments {
if s.Position == pos {
return rungRef{ID: s.ID, Title: s.Title, Branch: s.Branch, Target: s.Target, Preflight: s.Preflight,
Number: s.Number, Behind: s.Behind, Conflicts: s.PreflightConflicts, Waiting: s.Waiting, Here: s.ID == l.ID}, true
}
}
return rungRef{}, false
}
// Below is the rung this landing is stacked on, for the head's route;
// the zero rung when it stands on a plain branch.
func (l landingDetail) Below() rungRef {
if l.Position < 2 {
return rungRef{}
}
r, _ := l.rungAt(l.Position - 1)
return r
}
// Strip decides the decision strip. It reads the record as everyone
// sees it; what the reader may DO about it is the template's, from
// CanAccept and AcceptState.
func (l landingDetail) Strip() stripState {
if l.State != "open" {
return stripState{}
}
if l.LandingNow {
return stripState{State: "landing-now", Label: "Landing", Tone: "ok"}
}
if l.Protected && !l.MayLand && l.CanAccept {
return stripState{State: "protected", Label: "Protected", Tone: "bad"}
}
if len(l.Stack.Segments) > 1 {
if r, ok := l.rungAt(l.Stack.StopsAt); ok && l.Stack.StopsAt > 0 {
return stripState{State: "stack-stops", Label: "Blocked", Tone: "bad", Rung: r}
}
if r, ok := l.rungAt(l.Stack.WaitsAt); ok && l.Stack.WaitsAt > 0 {
return stripState{State: "stack-waits", Label: "Waiting", Tone: "warn", Rung: r}
}
}
switch l.Mergeable {
case "stacked":
if l.StackLandCount() > 0 {
return stripState{State: "stack-ready", Label: "Ready", Tone: "ok"}
}
return stripState{State: "stacked", Label: "Stacked"}
case "conflict":
return stripState{State: "conflict", Label: "Conflicts", Tone: "bad"}
case "up-to-date":
return stripState{State: "landed", Label: "Already in", Tone: "ok"}
}
if len(l.Unmet) > 0 {
return stripState{State: "checks", Label: "Waiting", Tone: "warn"}
}
if l.StaleCI && len(l.Required) > 0 {
return stripState{State: "stale", Label: "Stale", Tone: "warn"}
}
if l.Mergeable == "clean" {
return stripState{State: "ready", Label: "Ready", Tone: "ok"}
}
return stripState{State: "checking", Label: "Checking"}
}
// UnmetText lists the required checks that are not green yet.
func (l landingDetail) UnmetText() string { return strings.Join(l.Unmet, ", ") }