Skip to content
quarry Artifact [b07f90a00c]
Login

Artifact [b07f90a00c]

Artifact b07f90a00cce4df9d614ed389c5ddbd9e8f118496e53e7446f122cd1070f2d9b:


// poll.go parses the ```poll fenced block — quarry's poll convention.
// The DEFINITION lives in the forum post body, so it syncs like any
// artifact and a fossil peer renders it as readable markdown; only the
// ballots (OLTP) are forge-local. Full compatibility BY degradation.
package api

import (
	"strings"
	"time"
)

type pollDef struct {
	Question string
	Options  []string
	Closes   string // YYYY-MM-DD (UTC); "" never closes
}

// parsePoll extracts the first ```poll block. rest is the body with the
// block removed (quarry renders the widget instead); def is nil when no
// well-formed poll (a question and at least two options) is present —
// then the body renders untouched, exactly as a fossil peer would.
func parsePoll(body string) (def *pollDef, rest string) {
	lines := strings.Split(body, "\n")
	start := -1
	for i, l := range lines {
		if strings.TrimSpace(l) == "```poll" {
			start = i
			break
		}
	}
	if start < 0 {
		return nil, body
	}
	end := -1
	for i := start + 1; i < len(lines); i++ {
		if strings.TrimSpace(lines[i]) == "```" {
			end = i
			break
		}
	}
	if end < 0 {
		return nil, body
	}
	d := &pollDef{}
	for _, l := range lines[start+1 : end] {
		l = strings.TrimSpace(l)
		switch {
		case strings.HasPrefix(l, "question:"):
			d.Question = strings.TrimSpace(strings.TrimPrefix(l, "question:"))
		case strings.HasPrefix(l, "closes:"):
			d.Closes = strings.TrimSpace(strings.TrimPrefix(l, "closes:"))
		case strings.HasPrefix(l, "- "):
			d.Options = append(d.Options, strings.TrimSpace(strings.TrimPrefix(l, "- ")))
		}
	}
	if d.Question == "" || len(d.Options) < 2 {
		return nil, body
	}
	return d, strings.Join(append(append([]string{}, lines[:start]...), lines[end+1:]...), "\n")
}

// closed reports whether the poll has passed its close DATE: ballots
// are accepted through the named day (UTC), frozen after it.
func (d *pollDef) closed(now time.Time) bool {
	if d.Closes == "" {
		return false
	}
	t, err := time.Parse("2006-01-02", d.Closes)
	if err != nil {
		return false // an unreadable date never silently freezes a poll
	}
	return now.UTC().After(t.AddDate(0, 0, 1))
}