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

# Functions

> Built-in helper functions and custom template functions

The view engine provides built-in template functions that make your templates more expressive. This guide covers all available functions and how to add your own.

## Built-in Functions

These functions are available in all templates.

### Data Structure Functions

#### dict

Creates a map from key-value pairs:

```html theme={null}
{{$user := dict "Name" "Alice" "Age" 30 "Admin" true}}
<p>{{$user.Name}} is {{$user.Age}} years old</p>
```

#### list

Creates a slice from values:

```html theme={null}
{{$fruits := list "apple" "banana" "cherry"}}
<ul>
{{range $fruits}}
    <li>{{.}}</li>
{{end}}
</ul>
```

### String Functions

#### upper

Converts a string to uppercase:

```html theme={null}
<span class="status">{{upper .Data.Status}}</span>
<!-- "active" → "ACTIVE" -->
```

#### lower

Converts a string to lowercase:

```html theme={null}
<div class="type-{{lower .Data.Type}}">
<!-- "Featured" → "featured" -->
```

#### trim

Removes leading and trailing whitespace:

```html theme={null}
<p>{{trim .Data.Description}}</p>
```

#### contains

Checks if a string contains a substring:

```html theme={null}
{{if contains .Data.Email "@company.com"}}
    <span class="internal">Internal User</span>
{{end}}
```

#### replace

Replaces occurrences of a substring:

```html theme={null}
{{replace .Data.Content "\n" "<br>" -1}}
<!-- Replace all newlines with <br> -->
```

The third argument is the maximum number of replacements (-1 means all).

#### split

Splits a string into a slice:

```html theme={null}
{{$parts := split .Data.Tags ","}}
{{range $parts}}
    <span class="tag">{{trim .}}</span>
{{end}}
```

#### join

Joins a slice into a string:

```html theme={null}
<meta name="keywords" content="{{join .Data.Keywords ", "}}">
```

#### hasPrefix

Checks if a string starts with a prefix:

```html theme={null}
{{if hasPrefix .Data.URL "https"}}
    <span class="secure">Secure</span>
{{end}}
```

#### hasSuffix

Checks if a string ends with a suffix:

```html theme={null}
{{if hasSuffix .Data.Filename ".pdf"}}
    <span class="file-icon">PDF</span>
{{end}}
```

### Summary Table

| Function    | Description       | Example                                 |
| ----------- | ----------------- | --------------------------------------- |
| `dict`      | Create a map      | `{{dict "key" "value"}}`                |
| `list`      | Create a slice    | `{{list 1 2 3}}`                        |
| `upper`     | Uppercase string  | `{{upper .Data.Name}}`                  |
| `lower`     | Lowercase string  | `{{lower .Data.Name}}`                  |
| `trim`      | Trim whitespace   | `{{trim .Data.Text}}`                   |
| `contains`  | Check substring   | `{{if contains .Data.Text "hello"}}`    |
| `replace`   | Replace substring | `{{replace .Data.Text "old" "new" -1}}` |
| `split`     | Split string      | `{{split .Data.Tags ","}}`              |
| `join`      | Join slice        | `{{join .Data.Items ", "}}`             |
| `hasPrefix` | Check prefix      | `{{if hasPrefix .Data.URL "https"}}`    |
| `hasSuffix` | Check suffix      | `{{if hasSuffix .Data.File ".go"}}`     |

## Go's Standard Functions

Go's `html/template` package provides these built-in functions:

### Comparison Functions

| Function | Description           |
| -------- | --------------------- |
| `eq`     | Equal                 |
| `ne`     | Not equal             |
| `lt`     | Less than             |
| `le`     | Less than or equal    |
| `gt`     | Greater than          |
| `ge`     | Greater than or equal |

```html theme={null}
{{if eq .Data.Status "active"}}Active{{end}}
{{if gt .Data.Count 10}}Many items{{end}}
{{if le .Data.Stock 5}}Low stock{{end}}
```

### Logic Functions

| Function | Description |
| -------- | ----------- |
| `and`    | Logical AND |
| `or`     | Logical OR  |
| `not`    | Logical NOT |

```html theme={null}
{{if and .Data.LoggedIn .Data.IsAdmin}}Admin controls{{end}}
{{if or .Data.Error .Data.Warning}}Alert{{end}}
{{if not .Data.Disabled}}Enabled{{end}}
```

### Other Standard Functions

| Function   | Description                            |
| ---------- | -------------------------------------- |
| `len`      | Length of array, slice, map, or string |
| `index`    | Index into array, slice, or map        |
| `print`    | Format with default formatting         |
| `printf`   | Format with format string              |
| `println`  | Format with newline                    |
| `html`     | HTML escape                            |
| `js`       | JavaScript escape                      |
| `urlquery` | URL query escape                       |

```html theme={null}
<p>{{len .Data.Items}} items</p>
<p>First: {{index .Data.Items 0}}</p>
<p>{{printf "%.2f" .Data.Price}}</p>
```

## Adding Custom Functions

Add your own functions via the `Funcs` option:

```go theme={null}
import (
    "html/template"
    "strings"
    "time"

    "github.com/go-mizu/mizu/view"
)

engine := view.New(view.Config{
    Dir: "./views",
    Funcs: template.FuncMap{
        // Simple function
        "greet": func(name string) string {
            return "Hello, " + name + "!"
        },

        // Function with multiple arguments
        "pluralize": func(count int, singular, plural string) string {
            if count == 1 {
                return singular
            }
            return plural
        },

        // Date formatting
        "formatDate": func(t time.Time, layout string) string {
            return t.Format(layout)
        },

        // Word count
        "wordCount": func(s string) int {
            return len(strings.Fields(s))
        },

        // Truncate with ellipsis
        "truncate": func(s string, length int) string {
            if len(s) <= length {
                return s
            }
            return s[:length] + "..."
        },
    },
})
```

### Using Custom Functions

```html theme={null}
<h1>{{greet .Data.Name}}</h1>

<p>{{.Data.Count}} {{pluralize .Data.Count "item" "items"}}</p>

<p>Published: {{formatDate .Data.CreatedAt "January 2, 2006"}}</p>

<p>{{wordCount .Data.Content}} words</p>

<p>{{truncate .Data.Description 100}}</p>
```

## Common Custom Functions

### Date Formatting

```go theme={null}
"formatDate": func(t time.Time, layout string) string {
    return t.Format(layout)
},

"timeAgo": func(t time.Time) string {
    duration := time.Since(t)
    switch {
    case duration < time.Minute:
        return "just now"
    case duration < time.Hour:
        return fmt.Sprintf("%d minutes ago", int(duration.Minutes()))
    case duration < 24*time.Hour:
        return fmt.Sprintf("%d hours ago", int(duration.Hours()))
    default:
        return fmt.Sprintf("%d days ago", int(duration.Hours()/24))
    }
},
```

```html theme={null}
<time>{{formatDate .Data.CreatedAt "Jan 2, 2006"}}</time>
<span>{{timeAgo .Data.CreatedAt}}</span>
```

### Number Formatting

```go theme={null}
"formatNumber": func(n int) string {
    // Simple thousands separator
    s := strconv.Itoa(n)
    if len(s) <= 3 {
        return s
    }
    var result []byte
    for i, c := range s {
        if i > 0 && (len(s)-i)%3 == 0 {
            result = append(result, ',')
        }
        result = append(result, byte(c))
    }
    return string(result)
},

"formatCurrency": func(amount float64) string {
    return fmt.Sprintf("$%.2f", amount)
},
```

```html theme={null}
<p>{{formatNumber .Data.Views}} views</p>
<p>Price: {{formatCurrency .Data.Price}}</p>
```

### URL Helpers

```go theme={null}
"urlEncode": url.QueryEscape,

"activeClass": func(currentPath, linkPath string) string {
    if currentPath == linkPath {
        return "active"
    }
    return ""
},
```

```html theme={null}
<a href="/search?q={{urlEncode .Data.Query}}">Search</a>
<a href="/" class="{{activeClass .Data.CurrentPath "/"}}">Home</a>
```

### Safe HTML (Use Carefully!)

```go theme={null}
"safeHTML": func(s string) template.HTML {
    return template.HTML(s)
},
```

```html theme={null}
{{/* Only use with trusted content! */}}
{{safeHTML .Data.TrustedContent}}
```

### Default Values

```go theme={null}
"default": func(defaultVal, val interface{}) interface{} {
    if val == nil || val == "" || val == 0 {
        return defaultVal
    }
    return val
},
```

```html theme={null}
<img src="{{default "/default-avatar.png" .Data.Avatar}}">
```

## Best Practices

### 1. Keep Functions Pure

Functions should not have side effects:

```go theme={null}
// Good: pure function
"double": func(n int) int { return n * 2 },

// Bad: has side effects
"logAndReturn": func(s string) string {
    log.Println(s)  // Side effect!
    return s
},
```

### 2. Handle Edge Cases

```go theme={null}
"truncate": func(s string, length int) string {
    if s == "" || length <= 0 {
        return ""
    }
    if len(s) <= length {
        return s
    }
    return s[:length] + "..."
},
```

### 3. Use Pipelines for Readability

```html theme={null}
<!-- Good: pipeline style -->
<p>{{.Data.Bio | truncate 200 | upper}}</p>

<!-- Also fine: nested calls -->
<p>{{upper (truncate .Data.Bio 200)}}</p>
```

### 4. Document Your Functions

```go theme={null}
Funcs: template.FuncMap{
    // formatPhone formats a phone number as (XXX) XXX-XXXX
    // Example: "1234567890" → "(123) 456-7890"
    "formatPhone": formatPhone,

    // pluralize returns singular if count is 1, plural otherwise
    // Example: pluralize 1 "item" "items" → "item"
    "pluralize": pluralize,
}
```

### 5. Test Custom Functions

```go theme={null}
func TestTruncate(t *testing.T) {
    tests := []struct {
        input    string
        length   int
        expected string
    }{
        {"hello", 10, "hello"},
        {"hello world", 5, "hello..."},
        {"", 5, ""},
    }

    for _, tt := range tests {
        result := truncate(tt.input, tt.length)
        if result != tt.expected {
            t.Errorf("truncate(%q, %d) = %q, want %q",
                tt.input, tt.length, result, tt.expected)
        }
    }
}
```

### 6. Be Careful with safeHTML

Only use `safeHTML` with content you completely trust:

```html theme={null}
<!-- Safe: your own content -->
{{safeHTML .Data.TrustedContent}}

<!-- Dangerous: user content - let Go escape it -->
{{.Data.UserComment}}  <!-- Auto-escaped -->
```

## Complete Example

```go theme={null}
package main

import (
    "fmt"
    "html/template"
    "strings"
    "time"

    "github.com/go-mizu/mizu"
    "github.com/go-mizu/mizu/view"
)

func main() {
    engine := view.New(view.Config{
        Dir:           "./views",
        DefaultLayout: "default",
        Funcs: template.FuncMap{
            // String helpers
            "truncate": truncate,
            "slugify":  slugify,

            // Date helpers
            "formatDate": formatDate,
            "timeAgo":    timeAgo,

            // Number helpers
            "formatNumber":   formatNumber,
            "formatCurrency": formatCurrency,

            // Logic helpers
            "default":   defaultValue,
            "pluralize": pluralize,
        },
    })

    app := mizu.New()
    app.Use(engine.Middleware())

    // ... routes

    app.Listen(":8080")
}

func truncate(s string, length int) string {
    if len(s) <= length {
        return s
    }
    return s[:length] + "..."
}

func slugify(s string) string {
    return strings.ToLower(strings.ReplaceAll(s, " ", "-"))
}

func formatDate(t time.Time, layout string) string {
    return t.Format(layout)
}

func timeAgo(t time.Time) string {
    d := time.Since(t)
    switch {
    case d < time.Minute:
        return "just now"
    case d < time.Hour:
        return fmt.Sprintf("%dm ago", int(d.Minutes()))
    case d < 24*time.Hour:
        return fmt.Sprintf("%dh ago", int(d.Hours()))
    default:
        return fmt.Sprintf("%dd ago", int(d.Hours()/24))
    }
}

func formatNumber(n int) string {
    return fmt.Sprintf("%d", n) // Simplified
}

func formatCurrency(amount float64) string {
    return fmt.Sprintf("$%.2f", amount)
}

func defaultValue(defaultVal, val interface{}) interface{} {
    if val == nil || val == "" {
        return defaultVal
    }
    return val
}

func pluralize(count int, singular, plural string) string {
    if count == 1 {
        return singular
    }
    return plural
}
```
