Skip to content

Commit

Permalink
Add a function to return a policy whose output is the explanation. (#977
Browse files Browse the repository at this point in the history
)

Implements #974
  • Loading branch information
seirl authored Jul 22, 2024
1 parent bc96f3b commit 37d5b96
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 0 deletions.
38 changes: 38 additions & 0 deletions policy/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@ func (p *Policy) SetMetadata(name string, value any) {
p.metadata[name] = value
}

// GetExplanationOutputPolicy returns a copy of the policy, except the output of each match block
// is replaced by the expression in the explanation field.
func (p *Policy) GetExplanationOutputPolicy() *Policy {
ep := Policy{
name: p.name,
semantic: p.semantic,
info: &*p.info,
metadata: p.metadata,
source: &*p.source,
}
if p.rule != nil {
ep.rule = p.rule.getExplanationOutputRule()
}
return &ep
}

// NewRule creates a Rule instance.
func NewRule() *Rule {
return &Rule{
Expand Down Expand Up @@ -165,6 +181,28 @@ func (r *Rule) AddVariable(v *Variable) {
r.variables = append(r.variables, v)
}

func (r *Rule) getExplanationOutputRule() *Rule {
if r == nil {
return nil
}
er := Rule{
id: r.id,
description: r.description,
}
for _, variable := range r.variables {
er.variables = append(er.variables, &*variable)
}
for _, match := range r.matches {
em := Match{
condition: match.condition,
output: match.explanation,
rule: match.rule.getExplanationOutputRule(),
}
er.matches = append(er.matches, &em)
}
return &er
}

// NewVariable creates a variable instance.
func NewVariable() *Variable {
return &Variable{}
Expand Down
52 changes: 52 additions & 0 deletions policy/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,55 @@ rule:
}
}
}

func TestGetExplanationOutputPolicy(t *testing.T) {
tst := `
rule:
match:
- condition: "false"
rule:
match:
- condition: "1 > 2"
output: "false"
explanation: "'bad_inner'"
- output: "true"
explanation: "'good_inner'"
- output: "true"
explanation: "'good_outer'"
`

parser, err := NewParser()
if err != nil {
t.Fatalf("NewParser() failed: %v", err)
}
policy, iss := parser.Parse(StringSource(tst, "<input>"))
if iss != nil {
t.Fatalf("Parse() failed: %v", err)
}

explanationPolicy := policy.GetExplanationOutputPolicy()

want := "'bad_inner'"
got := explanationPolicy.Rule().Matches()[0].rule.Matches()[0].output.Value
if got != want {
t.Errorf("First inner output = %v, wanted %v", got, want)
}

want = "1 > 2"
got = explanationPolicy.Rule().Matches()[0].rule.Matches()[0].condition.Value
if got != want {
t.Errorf("First inner condition = %v, wanted %v", got, want)
}

want = "'good_inner'"
got = explanationPolicy.Rule().Matches()[0].rule.Matches()[1].output.Value
if got != want {
t.Errorf("Second inner output = %v, wanted %v", got, want)
}

want = "'good_outer'"
got = explanationPolicy.Rule().Matches()[1].output.Value
if got != want {
t.Errorf("Second outer output = %v, wanted %v", got, want)
}
}

0 comments on commit 37d5b96

Please sign in to comment.