Skip to content
quarry Artifact [bbd7aaedcf]
Login

Artifact [bbd7aaedcf]

Artifact bbd7aaedcfdf4beccef3e6811814da4a243268dabec32189900e6d5ef1f1b74c:


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, Position             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, Position: s.Position, 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, ", ") }