> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gorules.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Go Rules Engine

> Integrate GoRules into your Go application.

Install the ZEN Engine and evaluate your first decision in Go.

## Installation

```bash theme={null}
go get github.com/gorules/zen-go
```

## Basic usage

```go theme={null}
package main

import (
    "fmt"
    "os"

    zen "github.com/gorules/zen-go"
)

func main() {
    content, _ := os.ReadFile("./pricing-rules.json")

    engine := zen.NewEngine(zen.EngineConfig{})
    defer engine.Dispose()

    decision, _ := engine.CreateDecision(content)
    defer decision.Dispose()

    response, _ := decision.Evaluate(map[string]any{
        "customer": map[string]any{"tier": "gold", "yearsActive": 3},
        "order":    map[string]any{"subtotal": 150, "items": 5},
    })

    fmt.Println(string(response.Result))
    // => {"discount":0.15,"freeShipping":true}
}
```

## Loader

The loader pattern enables dynamic decision loading from any storage backend. Use `sync.Map` to cache decisions for optimal performance.

### File system

```go theme={null}
package main

import (
    "fmt"
    "os"
    "path/filepath"
    "sync"

    zen "github.com/gorules/zen-go"
)

var cache sync.Map

func loader(key string) ([]byte, error) {
    if data, ok := cache.Load(key); ok {
        return data.([]byte), nil
    }

    content, err := os.ReadFile(filepath.Join("./rules", key))
    if err != nil {
        return nil, err
    }

    cache.Store(key, content)
    return content, nil
}

func main() {
    engine := zen.NewEngine(zen.EngineConfig{Loader: loader})
    defer engine.Dispose()

    response, _ := engine.Evaluate("pricing.json", map[string]any{"amount": 100})
    fmt.Println(string(response.Result))
}
```

### AWS S3

```go theme={null}
package main

import (
    "archive/zip"
    "bytes"
    "context"
    "errors"
    "fmt"
    "io"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/s3"
    zen "github.com/gorules/zen-go"
)

var rules = make(map[string][]byte)

func main() {
    // Download and extract all decisions at startup
    cfg, _ := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-east-1"))
    s3Client := s3.NewFromConfig(cfg)

    result, _ := s3Client.GetObject(context.TODO(), &s3.GetObjectInput{
        Bucket: aws.String("my-rules-bucket"),
        Key:    aws.String("decisions.zip"),
    })
    defer result.Body.Close()

    zipBytes, _ := io.ReadAll(result.Body)
    zipReader, _ := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))

    for _, file := range zipReader.File {
        if !file.FileInfo().IsDir() {
            rc, _ := file.Open()
            content, _ := io.ReadAll(rc)
            rc.Close()
            rules[file.Name] = content
        }
    }

    engine := zen.NewEngine(zen.EngineConfig{
        Loader: func(key string) ([]byte, error) {
            if data, ok := rules[key]; ok {
                return data, nil
            }
            return nil, errors.New("decision not found: " + key)
        },
    })
    defer engine.Dispose()

    response, _ := engine.Evaluate("pricing.json", map[string]any{"amount": 100})
    fmt.Println(string(response.Result))
}
```

### Azure Blob Storage

```go theme={null}
package main

import (
    "archive/zip"
    "bytes"
    "context"
    "errors"
    "fmt"
    "io"
    "os"

    "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
    zen "github.com/gorules/zen-go"
)

var rules = make(map[string][]byte)

func main() {
    // Download and extract all decisions at startup
    client, _ := azblob.NewClientFromConnectionString(os.Getenv("AZURE_STORAGE_CONNECTION"), nil)
    containerClient := client.ServiceClient().NewContainerClient("rules")

    blobClient := containerClient.NewBlobClient("decisions.zip")
    resp, _ := blobClient.DownloadStream(context.TODO(), nil)
    defer resp.Body.Close()

    zipBytes, _ := io.ReadAll(resp.Body)
    zipReader, _ := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))

    for _, file := range zipReader.File {
        if !file.FileInfo().IsDir() {
            rc, _ := file.Open()
            content, _ := io.ReadAll(rc)
            rc.Close()
            rules[file.Name] = content
        }
    }

    engine := zen.NewEngine(zen.EngineConfig{
        Loader: func(key string) ([]byte, error) {
            if data, ok := rules[key]; ok {
                return data, nil
            }
            return nil, errors.New("decision not found: " + key)
        },
    })
    defer engine.Dispose()

    response, _ := engine.Evaluate("pricing.json", map[string]any{"amount": 100})
    fmt.Println(string(response.Result))
}
```

### Google Cloud Storage

```go theme={null}
package main

import (
    "archive/zip"
    "bytes"
    "context"
    "errors"
    "fmt"
    "io"

    "cloud.google.com/go/storage"
    zen "github.com/gorules/zen-go"
)

var rules = make(map[string][]byte)

func main() {
    // Download and extract all decisions at startup
    client, _ := storage.NewClient(context.TODO())
    bucket := client.Bucket("my-rules-bucket")

    reader, _ := bucket.Object("decisions.zip").NewReader(context.TODO())
    defer reader.Close()

    zipBytes, _ := io.ReadAll(reader)
    zipReader, _ := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))

    for _, file := range zipReader.File {
        if !file.FileInfo().IsDir() {
            rc, _ := file.Open()
            content, _ := io.ReadAll(rc)
            rc.Close()
            rules[file.Name] = content
        }
    }

    engine := zen.NewEngine(zen.EngineConfig{
        Loader: func(key string) ([]byte, error) {
            if data, ok := rules[key]; ok {
                return data, nil
            }
            return nil, errors.New("decision not found: " + key)
        },
    })
    defer engine.Dispose()

    response, _ := engine.Evaluate("pricing.json", map[string]any{"amount": 100})
    fmt.Println(string(response.Result))
}
```

## Error handling

```go theme={null}
response, err := decision.Evaluate(input)
if err != nil {
    log.Printf("Evaluation failed: %v", err)
    return err
}

fmt.Println(string(response.Result))
```

## Tracing

Enable tracing to inspect decision execution:

```go theme={null}
response, err := decision.EvaluateWithOpts(input, zen.EvaluationOptions{
    Trace: true,
})
if err != nil {
    return err
}

fmt.Println(response.Trace)
// Each node's input, output, and performance timing

fmt.Println(response.Performance)
// Total evaluation time
```

## Expression utilities

Evaluate ZEN expressions outside of a decision context:

```go theme={null}
import zen "github.com/gorules/zen-go"

// Standard expressions (with generics)
result, _ := zen.EvaluateExpression[int]("a + b", map[string]any{"a": 5, "b": 3})
// => 8

total, _ := zen.EvaluateExpression[int]("sum(items)", map[string]any{"items": []int{1, 2, 3, 4}})
// => 10

// Unary expressions (comparison against $)
isValid, _ := zen.EvaluateUnaryExpression(">= 5", map[string]any{"$": 10})
// => true

inList, _ := zen.EvaluateUnaryExpression("'US', 'CA', 'MX'", map[string]any{"$": "US"})
// => true
```

## Best practices

**Initialize the engine once.** Create a single `ZenEngine` instance at application startup and reuse it for all evaluations.

**Implement a loader for dynamic decisions.** The loader pattern centralizes decision loading logic and enables caching at the source.

**Call `Dispose()` on cleanup.** Release engine and decision resources when your application terminates.

```go theme={null}
engine := zen.NewEngine(zen.EngineConfig{Loader: loader})
defer engine.Dispose()
```

**Use goroutines for parallel evaluation.** Decision evaluation is thread-safe and works well with concurrent workloads.

```go theme={null}
var wg sync.WaitGroup
for _, input := range inputs {
    wg.Add(1)
    go func(in map[string]any) {
        defer wg.Done()
        response, _ := engine.Evaluate("pricing.json", in)
        // process response
    }(input)
}
wg.Wait()
```
