func exercise5() {
	fmt.Println("=== Exercise 5: Building JSON ===\n")
	env, _ := cel.NewEnv(
		cel.Declarations(
			decls.NewVar("now", decls.Timestamp),
		),
	)
	ast := compile(env, `
		{'aud': 'my-project',
		 'exp': now + duration('300s'),
		 'extra_claims': {
		 	'group': 'admin'
		 },
		 'iat': now,
		 'iss': 'auth.acme.com:12350',
		 'nbf': now,
		 'sub': 'serviceAccount:delegate@acme.co'
		 }`,
		decls.NewMapType(decls.String, decls.Dyn))
	program, _ := env.Program(ast)
	out, _, _ := eval(
		program,
		map[string]interface{}{
			"now": &tpb.Timestamp{Seconds: time.Now().Unix()},
		},
	)
	fmt.Printf("------ type conversion ------\n%v\n", valueToJSON(out))
	fmt.Println()
}
exercise5 covers how to build complex objects as CEL literals.

Given the input `now`, construct a JWT with an expiry of 5 minutes.
package main
import (
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"time"
	"github.com/golang/glog"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/encoding/prototext"
	"google.golang.org/protobuf/proto"
	"github.com/google/cel-go/cel"
	"github.com/google/cel-go/checker/decls"
	"github.com/google/cel-go/common/types"
	"github.com/google/cel-go/common/types/ref"
	"github.com/google/cel-go/common/types/traits"
	"github.com/google/cel-go/interpreter/functions"
	exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
	rpcpb "google.golang.org/genproto/googleapis/rpc/context/attribute_context"
	structpb "google.golang.org/protobuf/types/known/structpb"
	tpb "google.golang.org/protobuf/types/known/timestamppb"
)
func main() {
	exercise1()
	exercise2()
	exercise3()
	exercise4()
	exercise5()
	exercise6()
	exercise7()
	exercise8()
}
func exercise1() {
	fmt.Println("=== Exercise 1: Hello World ===\n")
	env, err := cel.NewEnv()
	if err != nil {
		glog.Exitf("env error: %v", err)
	}
	ast, iss := env.Parse(`"Hello, World!"`)
	if iss.Err() != nil {
		glog.Exit(iss.Err())
	}
	checked, iss := env.Check(ast)
	if iss.Err() != nil {
		glog.Exit(iss.Err())
	}
	if !proto.Equal(checked.ResultType(), decls.String) {
		glog.Exitf(
			"Got %v, wanted %v result type",
			checked.ResultType(), decls.String)
	}
	program, err := env.Program(checked)
	if err != nil {
		glog.Exitf("program error: %v", err)
	}
	eval(program, cel.NoVars())
	fmt.Println()
}
func exercise2() {
	fmt.Println("=== Exercise 2: Variables ===\n")
	env, err := cel.NewEnv(
		cel.Types(&rpcpb.AttributeContext_Request{}),
		cel.Declarations(
			decls.NewVar("request",
				decls.NewObjectType("google.rpc.context.AttributeContext.Request")),
		),
	)
	if err != nil {
		glog.Exit(err)
	}
	ast := compile(env, `request.auth.claims.group == 'admin'`, decls.Bool)
	program, _ := env.Program(ast)
	claims := map[string]string{"group": "admin"}
	eval(program, request(auth("user:me@acme.co", claims), time.Now()))
	fmt.Println()
}
func exercise3() {
	fmt.Println("=== Exercise 3: Logical AND/OR ===\n")
	env, _ := cel.NewEnv(
		cel.Types(&rpcpb.AttributeContext_Request{}),
		cel.Declarations(
			decls.NewVar("request",
				decls.NewObjectType("google.rpc.context.AttributeContext.Request"),
			),
		),
	)
	ast := compile(env,
		`request.auth.claims.group == 'admin'
				|| request.auth.principal == 'user:me@acme.co'`,
		decls.Bool)
	program, _ := env.Program(ast)
	eval(program, request(auth("user:me@acme.co", emptyClaims), time.Now()))
	eval(program, request(auth("other:me@acme.co", emptyClaims), time.Now()))
	fmt.Println()
}
func exercise4() {
	fmt.Println("=== Exercise 4: Customization ===\n")
	typeParamA := decls.NewTypeParamType("A")
	typeParamB := decls.NewTypeParamType("B")
	mapAB := decls.NewMapType(typeParamA, typeParamB)
	env, _ := cel.NewEnv(
		cel.Types(&rpcpb.AttributeContext_Request{}),
		cel.Declarations(
			decls.NewVar("request",
				decls.NewObjectType("google.rpc.context.AttributeContext.Request"),
			),
			decls.NewFunction("contains",
				decls.NewParameterizedInstanceOverload(
					"map_contains_key_value",
					[]*exprpb.Type{mapAB, typeParamA, typeParamB},
					decls.Bool,
					[]string{"A", "B"},
				),
			),
		),
	)
	ast := compile(env,
		`request.auth.claims.contains('group', 'admin')`,
		decls.Bool)
	program, err := env.Program(ast,
		cel.Functions(
			&functions.Overload{
				Operator: "map_contains_key_value",
				Function: mapContainsKeyValue,
			}),
	)
	if err != nil {
		glog.Exit(err)
	}
	eval(program, request(auth("user:me@acme.co", emptyClaims), time.Now()))
	claims := map[string]string{"group": "admin"}
	eval(program, request(auth("user:me@acme.co", claims), time.Now()))
	fmt.Println()
}