Skip to content
quarry Artifact [edc24c60ad]
Login

Artifact [edc24c60ad]

Artifact edc24c60ad1ee4bdbff983b9e0d22521be978588804ba716c311858133f6d975:


package index

// strata.go — RFC 0006: a stack of dependent landings, derived and never
// stored. Follow target → branch across landings: a landing whose target
// is another landing's branch is a segment stacked on it; the one whose
// target is a plain branch is the base. The chain is read fresh from the
// summaries every time, so a segment retargeted by zit#144 after its base
// lands simply stops being one.

// StackSegment is one rung of a strata as the record page draws it.
type StackSegment struct {
	ID       string `json:"id"`
	Title    string `json:"title"`
	Branch   string `json:"branch"`
	Target   string `json:"target"`
	State    string `json:"state"`
	Position int    `json:"position"` // 1 is the base
	// Landable marks the one open segment whose target is not itself an
	// open landing's branch — the only rung accept can act on.
	Landable bool `json:"landable"`
}

// Strata is a chain, bottom-up.
type Strata struct {
	Base string `json:"base"`
	// Top is the highest rung: landing "the strata" means landing up
	// to it.
	Top      string         `json:"top"`
	Segments []StackSegment `json:"segments"`
	// Broken names why the chain cannot be walked: a cycle, or a base
	// that was rejected so everything above it is stacked on work
	// nobody will land (RFC 0006 §An abandoned base is flagged).
	Broken string `json:"broken,omitempty"`
}

// AttachStrata computes, for every OPEN landing in ls, which stack it
// belongs to and where: Stack (the base's id), Position and Depth. The
// input is every landing the caller knows about, whatever its state,
// because a segment's target may be the branch of a landing that was
// rejected — and that is a fact the chain must report, not lose.
//
// Superseded landings take no part: the current landing on a branch is
// the one the chain runs through.
func AttachStrata(ls []LandingSummary) {
	byBranch := map[string]*LandingSummary{}
	for i := range ls {
		l := &ls[i]
		if l.SupersededBy != "" {
			continue
		}
		if cur, ok := byBranch[l.Branch]; !ok || l.State == "open" && cur.State != "open" {
			byBranch[l.Branch] = l
		}
	}
	for i := range ls {
		l := &ls[i]
		l.Stack, l.Position, l.Depth, l.Broken = "", 0, 0, ""
		if l.State != "open" || l.SupersededBy != "" {
			continue
		}
		// Walk down to the base, counting.
		seen := map[string]bool{l.ID: true}
		base, pos := l, 1
		for {
			below, ok := byBranch[base.Target]
			if !ok {
				break // a plain branch: this is the base
			}
			if seen[below.ID] {
				l.Broken = "cycle"
				break
			}
			if below.State != "open" {
				if below.State == "rejected" {
					l.Broken = "base rejected"
				}
				// Accepted and not yet retargeted: the rung below is
				// done and this one is next. It counts as the base of
				// what remains.
				break
			}
			seen[below.ID] = true
			base, pos = below, pos+1
		}
		if pos == 1 && !isStackedOn(byBranch, l) {
			continue // standalone: no stack at all
		}
		l.Stack, l.Position = base.ID, pos
	}
	// Depth is the tallest position sharing a base.
	depth := map[string]int{}
	for i := range ls {
		if ls[i].Stack != "" && ls[i].Position > depth[ls[i].Stack] {
			depth[ls[i].Stack] = ls[i].Position
		}
	}
	for i := range ls {
		if ls[i].Stack != "" {
			ls[i].Depth = depth[ls[i].Stack]
		}
	}
}

// isStackedOn reports whether some open landing targets l's branch.
func isStackedOn(byBranch map[string]*LandingSummary, l *LandingSummary) bool {
	for _, other := range byBranch {
		if other.State == "open" && other.Target == l.Branch && other.ID != l.ID {
			return true
		}
	}
	return false
}

// StrataOf assembles the chain a landing belongs to, bottom-up, for the
// record page's map. Empty for a standalone landing. ls must already
// carry AttachStrata's reading.
func StrataOf(ls []LandingSummary, id string) Strata {
	var me *LandingSummary
	for i := range ls {
		if ls[i].ID == id {
			me = &ls[i]
		}
	}
	if me == nil || me.Stack == "" {
		return Strata{}
	}
	out := Strata{Base: me.Stack, Broken: me.Broken}
	for _, l := range ls {
		if l.Stack != me.Stack {
			continue
		}
		out.Segments = append(out.Segments, StackSegment{
			ID: l.ID, Title: l.Title, Branch: l.Branch, Target: l.Target,
			State: l.State, Position: l.Position, Landable: l.Position == 1 && l.Broken == "",
		})
	}
	// Bottom-up, by position.
	for i := 1; i < len(out.Segments); i++ {
		for j := i; j > 0 && out.Segments[j].Position < out.Segments[j-1].Position; j-- {
			out.Segments[j], out.Segments[j-1] = out.Segments[j-1], out.Segments[j]
		}
	}
	if n := len(out.Segments); n > 0 {
		out.Top = out.Segments[n-1].ID
	}
	return out
}

// Decisions counts what awaits a decision: every open landing that is
// not stacked on another open one. A stack of five is one decision —
// the base's — because nothing above it can be acted on until it is.
func Decisions(ls []LandingSummary) int {
	n := 0
	for _, l := range ls {
		if l.State == "open" && l.SupersededBy == "" && (l.Stack == "" || l.Position == 1) {
			n++
		}
	}
	return n
}