Skip to content
go-libfossil Artifact [28f52cc529]
Login

Artifact [28f52cc529]

Artifact 28f52cc5297b30d661b6a3834bea72e689803838eb40269cebbbbbda5eb021cf:


package sync

import (
	"context"
	"errors"
	"fmt"

	"github.com/danmestas/go-libfossil/db"
	"github.com/danmestas/go-libfossil/internal/cone"
	"github.com/danmestas/go-libfossil/internal/content"
	libfossil "github.com/danmestas/go-libfossil/internal/fsltype"
	"github.com/danmestas/go-libfossil/internal/xfer"
)

// sparseKeep is request-local. Unknown non-file artifacts remain dense; a file
// shared by covered and uncovered names is kept under either name.
type sparseKeep struct {
	cone  *cone.Cone
	out   map[libfossil.FslID]bool
	in    map[libfossil.FslID]bool
	nodes map[string]bool
}

func newSparseKeep(ctx context.Context, q db.Querier, c *cone.Cone) (*sparseKeep, error) {
	if c == nil {
		return nil, nil
	}
	k := &sparseKeep{cone: c, out: map[libfossil.FslID]bool{}, in: map[libfossil.FslID]bool{}, nodes: map[string]bool{}}
	rows, err := q.Query(`SELECT m.fid,f.name FROM mlink m JOIN filename f ON f.fnid=m.fnid WHERE m.fid>0
 UNION SELECT m.pid,f.name FROM mlink m JOIN filename f ON f.fnid=m.fnid WHERE m.pid>0`)
	if err != nil {
		return nil, fmt.Errorf("sparse references: %w", err)
	}
	for rows.Next() {
		if err := ctx.Err(); err != nil {
			return nil, errors.Join(err, rows.Close())
		}
		var rid libfossil.FslID
		var name string
		if err := rows.Scan(&rid, &name); err != nil {
			return nil, errors.Join(err, rows.Close())
		}
		if c.Covers(name) {
			k.in[rid] = true
		} else {
			k.out[rid] = true
		}
	}
	if err := errors.Join(rows.Err(), rows.Close()); err != nil {
		return nil, err
	}
	return k, nil
}

func (h *handler) ensureSparseKeep() error {
	if h.cone == nil || h.keep != nil {
		return nil
	}
	ctx := h.ctx
	if ctx == nil {
		ctx = context.Background()
	}
	var err error
	h.keep, err = newSparseKeep(ctx, h.repo.DB(), h.cone)
	return err
}

func (k *sparseKeep) keeps(rid libfossil.FslID) bool {
	return k == nil || !k.out[rid] || k.in[rid]
}

func coneContains(have, want *cone.Cone) bool {
	if have == nil {
		return true
	}
	if want == nil {
		return false
	}
	// Widening beneath an excluded interval cannot be certified by the root
	// member alone. Probe every policy boundary, including re-inclusions.
	for _, m := range want.Include() {
		if !have.Covers(m) {
			return false
		}
	}
	for _, m := range have.Exclude() {
		if want.Covers(m) {
			return false
		}
	}
	return true
}

// localConePragmas reads policy at request time so a widening affects the
// ordinary synchronization path rather than needing a second transfer protocol.
func localConePragmas(q db.Querier) ([]xfer.Card, error) {
	c, err := content.StoreCone(q)
	if err != nil {
		return nil, err
	}
	return conePragmas(c), nil
}