{"id":19322668,"url":"https://github.com/picatz/xcel","last_synced_at":"2025-07-12T15:06:05.948Z","repository":{"id":194145463,"uuid":"689904975","full_name":"picatz/xcel","owner":"picatz","description":"⚗️ Extend CEL expressions with custom objects.","archived":false,"fork":false,"pushed_at":"2024-07-03T01:41:56.000Z","size":24,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-06T05:42:52.075Z","etag":null,"topics":["cel","go"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/picatz/xcel","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/picatz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-11T06:42:20.000Z","updated_at":"2024-07-03T01:42:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"223d4aff-1e1a-4c83-acdb-ca0621780725","html_url":"https://github.com/picatz/xcel","commit_stats":null,"previous_names":["picatz/xcel"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/picatz%2Fxcel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/picatz%2Fxcel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/picatz%2Fxcel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/picatz%2Fxcel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/picatz","download_url":"https://codeload.github.com/picatz/xcel/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240427139,"owners_count":19799466,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cel","go"],"created_at":"2024-11-10T01:42:04.584Z","updated_at":"2025-02-24T05:41:47.382Z","avatar_url":"https://github.com/picatz.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xcel\n\nExtend [`CEL`](https://github.com/google/cel-spec) expressions with custom (native Go) objects and functions.\n\n## Usage\n\n```console\n$ go get github.com/picatz/xcel@latest\n```\n\n## Example\n\n```go\ntype Person struct {\n\tName string\n\tAge  int\n}\n\nperson := \u0026Person{\n\tName: \"test\",\n\tAge:  -1,\n}\n\nta, tp := xcel.NewTypeAdapter(), xcel.NewTypeProvider()\n\nobj, typ := xcel.NewObject(person)\n\nxcel.RegisterObject(ta, tp, obj, typ, map[string]*types.FieldType{\n\t\"name\": {\n\t\tType: types.StringType,\n\t\tIsSet: ref.FieldTester(func(target any) bool {\n\t\t\tx := target.(*xcel.Object[*Person])\n\n\t\t\tif x.Raw == nil || x.Raw.Name == \"\" {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn true\n\t\t}),\n\t\tGetFrom: ref.FieldGetter(func(target any) (any, error) {\n\t\t\tx := target.(*xcel.Object[*Person])\n\n\t\t\tif x.Raw == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"celval: object is nil\")\n\t\t\t}\n\n\t\t\treturn x.Raw.Name, nil\n\t\t}),\n\t},\n\t\"age\": {\n\t\tType: types.IntType,\n\t\tIsSet: ref.FieldTester(func(target any) bool {\n\t\t\tx := target.(*xcel.Object[*Person])\n\n\t\t\tif x.Raw == nil || x.Raw.Age \u003c 0 {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn true\n\t\t}),\n\t\tGetFrom: ref.FieldGetter(func(target any) (any, error) {\n\t\t\tx := target.(*xcel.Object[*Person])\n\n\t\t\tif x.Raw == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"celval: object is nil\")\n\t\t\t}\n\n\t\t\treturn x.Raw.Age, nil\n\t\t}),\n\t},\n})\n\nenv, _ := cel.NewEnv(\n    cel.Types(typ),\n    cel.Variable(\"obj\", typ),\n    cel.CustomTypeAdapter(ta),\n    cel.CustomTypeProvider(tp),\n)\n\nast, _ := env.Compile(\"obj.name == 'test' \u0026\u0026 obj.age \u003e 0\")\n\nprg, _ := env.Program(ast)\n\nout, _, _ := prg.Eval(map[string]any{\n    \"obj\": obj,\n})\n\nfmt.Println(out.Value())\n// Output: false\n```\n\n### Object Fields via Reflection\n\nFields can be registered via reflection, which is a bit more concise, but less flexible and less performant:\n\n```go\ntype Person struct {\n\tName string\n\tAge  int\n}\n\nperson := \u0026Person{\n\tName: \"test\",\n\tAge:  -1,\n}\n\nta, tp := xcel.NewTypeAdapter(), xcel.NewTypeProvider()\n\nobj, typ := xcel.NewObject(person)\n\nxcel.RegisterObject(ta, tp, obj, typ, xcel.NewFields(obj))\n```\n\n#### Benchmarks\n\nShowing some minimal performance differences between manual fields and reflection based fields for the same object:\n\n```go\nex := \u0026Example{\n\tName: \"test\",\n\tAge:  1,\n\tTags: []string{\"test\"},\n\tParent: \u0026Example{\n\t\tName: \"root\",\n\t\tAge:  -1,\n\t\tTags: []string{\"a\", \"b\", \"c\"},\n\t},\n\tPressure: 1.5,\n\tFn: func(i int) string {\n\t\treturn fmt.Sprintf(\"~%d~\", i)\n\t},\n\tBlob: []byte(\"test\"),\n}\n```\n\n\u003c!-- This is a CEL expression, not JavaScript. But, it is close enough. --\u003e\n\n```javascript\nobj.name == 'test' \u0026\u0026 obj.age \u003e 0 \u0026\u0026 ('test' in obj.tags) \u0026\u0026 obj.parent.name == 'root' \u0026\u0026 obj.pressure \u003e 1.0 \u0026\u0026 obj.fn(1) == '~1~' \u0026\u0026 has(obj.blob)\n```\n\n```console\n$ go test -benchmem -run=^$ -bench ^BenchmarkNewObject github.com/picatz/xcel -v\ngoos: darwin\ngoarch: arm64\npkg: github.com/picatz/xcel\nBenchmarkNewObjectManualFields\nBenchmarkNewObjectManualFields-8          677050              1620 ns/op             784 B/op         22 allocs/op\nBenchmarkNewObjectReflectionFields\nBenchmarkNewObjectReflectionFields-8      546022              2138 ns/op             880 B/op         31 allocs/op\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpicatz%2Fxcel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpicatz%2Fxcel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpicatz%2Fxcel/lists"}