// Copyright 2021 The OPA Authors. All rights reserved. // Use of this source code is governed by an Apache2 // license that can be found in the LICENSE file. package topdown import ( "fmt" "io" "strings" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" "github.com/open-policy-agent/opa/v1/topdown/print" ) func NewPrintHook(w io.Writer) print.Hook { return printHook{w: w} } type printHook struct { w io.Writer } func (h printHook) Print(_ print.Context, msg string) error { _, err := fmt.Fprintln(h.w, msg) return err } func builtinPrint(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { if bctx.PrintHook == nil { return iter(nil) } arr, err := builtins.ArrayOperand(operands[0].Value, 1) if err != nil { return err } buf := make([]string, arr.Len()) err = builtinPrintCrossProductOperands(bctx.Location, buf, arr, 0, func(buf []string) error { pctx := print.Context{ Context: bctx.Context, Location: bctx.Location, } return bctx.PrintHook.Print(pctx, strings.Join(buf, " ")) }) if err != nil { return err } return iter(nil) } func builtinPrintCrossProductOperands(loc *ast.Location, buf []string, operands *ast.Array, i int, f func([]string) error) error { if i >= operands.Len() { return f(buf) } operand := operands.Elem(i) // We allow primitives ... switch x := operand.Value.(type) { case ast.String: buf[i] = string(x) return builtinPrintCrossProductOperands(loc, buf, operands, i+1, f) case ast.Number, ast.Boolean, ast.Null: buf[i] = x.String() return builtinPrintCrossProductOperands(loc, buf, operands, i+1, f) } // ... but all other operand types must be sets. xs, ok := operand.Value.(ast.Set) if !ok { return Halt{Err: internalErr(loc, "illegal argument type: "+ast.ValueName(operand.Value))} } if xs.Len() == 0 { buf[i] = "" return builtinPrintCrossProductOperands(loc, buf, operands, i+1, f) } return xs.Iter(func(x *ast.Term) error { switch v := x.Value.(type) { case ast.String: buf[i] = string(v) default: buf[i] = v.String() } return builtinPrintCrossProductOperands(loc, buf, operands, i+1, f) }) } func init() { RegisterBuiltinFunc(ast.InternalPrint.Name, builtinPrint) }