Skip to content
go-libfossil Artifact [b104477c3b]
Login

Artifact [b104477c3b]

Artifact b104477c3b4c0976cb638038236098f29776cc07c54d960e958838e5d5e89654:


package merge

import (
	"bufio"
	"path/filepath"
	"strings"

	"github.com/danmestas/go-libfossil/internal/content"
	libfossil "github.com/danmestas/go-libfossil/internal/fsltype"
	"github.com/danmestas/go-libfossil/internal/manifest"
	"github.com/danmestas/go-libfossil/internal/repo"
)

// PatternRule maps a glob pattern to a strategy name.
type PatternRule struct {
	Glob     string
	Strategy string
}

// Resolver picks the merge strategy for a given filename.
type Resolver struct {
	patterns []PatternRule
	fallback string
}

// LoadResolver reads the .libfossil-merge file from the repo at the given
// version, plus the merge-strategy config key as fallback.
func LoadResolver(r *repo.Repo, tipRid libfossil.FslID) *Resolver {
	if r == nil {
		panic("merge.LoadResolver: r must not be nil")
	}
	res := &Resolver{fallback: "three-way"}

	var cfgDefault string
	if r.DB().QueryRow("SELECT value FROM config WHERE name='merge-strategy'").Scan(&cfgDefault) == nil && cfgDefault != "" {
		res.fallback = cfgDefault
	}

	if tipRid > 0 {
		f, found, err := manifest.LookupFile(r, tipRid, ".libfossil-merge")
		if err == nil && found {
			if rid, ok := content.AvailableByUUID(r.DB(), f.UUID); ok {
				// Bare Expand: one config file (.libfossil-merge) at one
				// version, read once. No revision walk to cache.
				if data, err := content.Expand(r.DB(), rid); err == nil {
					res.patterns = parseMergeFile(data)
				}
			}
		}
	}

	return res
}

// Resolve returns the strategy name for a filename.
func (res *Resolver) Resolve(filename string) string {
	for _, p := range res.patterns {
		matched, matchErr := filepath.Match(p.Glob, filepath.Base(filename))
		if matchErr != nil {
			continue
		}
		if matched {
			return p.Strategy
		}
	}
	return res.fallback
}

func parseMergeFile(data []byte) []PatternRule {
	var rules []PatternRule
	scanner := bufio.NewScanner(strings.NewReader(string(data)))
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		parts := strings.Fields(line)
		if len(parts) >= 2 {
			rules = append(rules, PatternRule{Glob: parts[0], Strategy: parts[1]})
		}
	}
	return rules
}