Vectrum
Sign In
77 lines · 2.1 KB · Go
package ccsettings

// Matching is approval.ParseRule's, the one reading of Claude Code's rule
// grammar in Codey: a rule in a settings file means what the same rule means
// given to --allowedTools, rather than two parsers drifting apart on what
// Bash(git:*) covers. This file only adapts it to the two questions a rule
// set asks.

import (
	"encoding/json"

	"github.com/johannSo/codey/internal/approval"
)

// Match is the result of checking one rule against one call.
type Match int

const (
	NoMatch Match = iota
	Matched
	// Unsure means the call cannot be pinned down well enough to say — an
	// opaque shell command, a word built at run time. An allow rule treats it
	// as no match; a deny or ask rule treats it as a reason to ask.
	Unsure
)

// matchMode is which kind of rule is asking. It matters for compound shell
// commands: an allow rule covers "a && b" only if it covers both, while a
// deny or ask rule catches it if it catches either — and catches a command it
// cannot see all the way through if anything that can be read out of it
// matches.
type matchMode int

const (
	forAllow matchMode = iota
	forRestrict
)

// compiled is one parsed rule.
type compiled interface {
	Match(tool string, input json.RawMessage, mode matchMode) Match
}

// matchEnv is what relative rule paths are resolved against.
type matchEnv struct {
	home, root string
}

// compile parses a rule. An error means the rule names something Codey has no
// equivalent for, or uses a specifier it does not understand; the caller
// reports it and the rule takes no part.
func compile(rule string, env matchEnv) (compiled, error) {
	r, err := approval.ParseRule(rule)
	if err != nil {
		return nil, err
	}
	r.Root, r.Home = env.root, env.home
	return shared{r}, nil
}

type shared struct{ r approval.Rule }

func (s shared) Match(tool string, input json.RawMessage, mode matchMode) Match {
	if mode == forAllow {
		if s.r.Matches(tool, input) {
			return Matched
		}
		return NoMatch
	}
	switch {
	case s.r.Touches(tool, input):
		return Matched
	case s.r.Unsure(tool, input):
		return Unsure
	}
	return NoMatch
}