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
}