# dotmod

**Immutable modifications to nested data structures**

Part of the Shape pillar, `dotmod` provides surgical, immutable modifications to nested data structures using dot notation paths.

## Overview

`dotmod` enables you to modify values deep within nested structures without mutating the original data. All operations return deep copies -- the original data is never altered.

## Core Operations

### set_ - Set a value at a path

```python
from shape.dotmod import set_

data = {
    "user": {
        "name": "Alice",
        "role": "user"
    }
}

# Create a new structure with modified value
new_data = set_(data, "user.role", "admin")

# Original unchanged
print(data["user"]["role"])      # "user"
print(new_data["user"]["role"])  # "admin"
```

### delete_ - Remove a path

```python
from shape.dotmod import delete_

data = {
    "user": {
        "name": "Alice",
        "email": "alice@example.com",
        "temp_token": "xyz123"
    }
}

# Remove temporary token
clean_data = delete_(data, "user.temp_token")
# {"user": {"name": "Alice", "email": "alice@example.com"}}
```

### update_ - Merge a dictionary into a path

`update_` performs a **dictionary merge** into an existing dictionary at the specified path. The `value` argument must be a `dict`; passing a non-dict raises `TypeError`.

```python
from shape.dotmod import update_

data = {
    "user": {
        "name": "Alice",
        "settings": {
            "theme": "light",
            "language": "en"
        }
    }
}

# Merge new keys into the settings dict
result = update_(data, "user.settings", {"theme": "dark", "font_size": 14})
# {"user": {"name": "Alice", "settings": {"theme": "dark", "language": "en", "font_size": 14}}}
```

### append_ - Append a value to a list

```python
from shape.dotmod import append_

data = {
    "user": {
        "tags": ["admin", "active"]
    }
}

# Append to the tags list
result = append_(data, "user.tags", "verified")
# {"user": {"tags": ["admin", "active", "verified"]}}
```

## Creating Nested Structures

`set_` automatically creates intermediate structures as needed:

```python
from shape.dotmod import set_

data = {}

# Creates nested structure
result = set_(data, "user.profile.settings.theme", "dark")
# {
#     "user": {
#         "profile": {
#             "settings": {
#                 "theme": "dark"
#             }
#         }
#     }
# }
```

## Working with Lists

Modify list elements by index:

```python
data = {
    "items": [
        {"id": 1, "status": "pending"},
        {"id": 2, "status": "pending"},
        {"id": 3, "status": "pending"}
    ]
}

# Update second item's status
result = set_(data, "items.1.status", "completed")

# Extend lists automatically
result = set_(data, "items.3.status", "new")
# Automatically extends the list with None padding
```

## Immutability via Deep Copy

All operations use `deepcopy` to produce an entirely new structure. There is no structural sharing -- every operation creates a full independent copy of the data.

```python
from shape.dotmod import set_

original = {
    "a": {"x": 1, "y": 2},
    "b": {"x": 3, "y": 4}
}

# Modify a.x
modified = set_(original, "a.x", 10)

# Both branches are deep-copied
assert original["a"] is not modified["a"]
assert original["b"] is not modified["b"]
```

## Real-World Examples

### Configuration Updates
```python
from shape.dotmod import set_

config = {
    "server": {
        "host": "localhost",
        "port": 8080,
        "ssl": False
    }
}

# Update for production (chain calls explicitly)
prod_config = set_(config, "server.host", "api.example.com")
prod_config = set_(prod_config, "server.port", 443)
prod_config = set_(prod_config, "server.ssl", True)
```

### State Management
```python
from shape.dotmod import update_

app_state = {
    "ui": {
        "theme": "light",
        "sidebar": True
    }
}

# Merge new settings into the ui dict
new_state = update_(app_state, "ui", {"theme": "dark"})
```

### Data Sanitization
```python
from shape.dotmod import delete_

user_data = {
    "profile": {
        "name": "Alice",
        "email": "alice@example.com",
        "password": "secret123",
        "ssn": "123-45-6789"
    }
}

# Remove sensitive fields
public_data = delete_(user_data, "profile.password")
public_data = delete_(public_data, "profile.ssn")
```

## Command Line

```bash
# Set a value at a path
echo '{"user": {"name": "Alice"}}' | dotmod set user.role '"admin"'

# Delete a path
echo '{"user": {"name": "Alice", "tmp": "x"}}' | dotmod delete user.tmp

# Merge a dict into a path (update)
echo '{"settings": {"theme": "light"}}' | dotmod update settings '{"font_size": 14}'

# Append to a list
echo '{"tags": ["a", "b"]}' | dotmod append tags '"c"'

# Read from a file
dotmod data.json set user.role '"admin"'

# YAML input (inferred from extension, or use --in yaml)
dotmod config.yaml set server.port 443
```

### Subcommands

| Subcommand | Description |
|------------|-------------|
| `set`      | Set a value at a path, creating it if necessary |
| `delete`   | Delete a value at a path |
| `update`   | Merge a JSON/YAML object into a dictionary at a path |
| `append`   | Append a value to a list at a path |

### Options

| Flag | Description |
|------|-------------|
| `data_file` | Path to source JSON/YAML file, or `-` for stdin (default) |
| `--in`, `--input-format` | Input format: `json` or `yaml` (inferred from extension if omitted) |

Values for `set`, `update`, and `append` are parsed as JSON or YAML.

## Integration with Other Tools

```python
from depth.dotget import get
from shape.dotmod import set_
from truth.dotexists.core import check

def safe_increment(data, path, amount=1):
    """Safely increment a numeric value."""
    if check(data, path):
        current = get(data, path) or 0
        return set_(data, path, current + amount)
    return set_(data, path, amount)
```

## Related Tools

- **[dotbatch](dotbatch.md)** - Atomic batch operations
- **[dotpipe](dotpipe.md)** - Chain transformations
- **[dotget](../depth/dotget.md)** - Read values from paths
