# `ExDav.Storage`
[🔗](https://git.sr.ht/~sbr/ExDav)

Behaviour for CalDAV storage adapters.

Adapters return plain maps so the protocol layer is agnostic of any
particular ORM. A reference Postgres adapter ships in
`ExDav.Storage.Postgres`.

Adapters are paired with the plug via the `:storage` opt:

    plug ExDav.CalDav.Plug,
      storage: ExDav.Storage.Postgres,
      authenticator: {ExDav.Authenticator.Basic,
                      verify: {ExDav.Storage.Postgres, :authenticate}}

# `calendar`

```elixir
@type calendar() :: %{
  name: String.t(),
  displayname: String.t(),
  description: String.t() | nil,
  components: [String.t()],
  ctag: non_neg_integer(),
  objects: %{optional(String.t()) =&gt; object()}
}
```

# `calendar_name`

```elixir
@type calendar_name() :: String.t()
```

# `create_calendar_opts`

```elixir
@type create_calendar_opts() :: [
  displayname: String.t() | nil,
  description: String.t() | nil,
  components: [String.t()]
]
```

# `object`

```elixir
@type object() :: %{
  name: String.t(),
  ical: String.t(),
  etag: String.t(),
  uid: String.t() | nil,
  component: String.t() | nil
}
```

# `object_name`

```elixir
@type object_name() :: String.t()
```

# `update_props`

```elixir
@type update_props() :: [displayname: String.t(), description: String.t() | nil]
```

# `username`

```elixir
@type username() :: String.t()
```

# `create_calendar`

```elixir
@callback create_calendar(username(), calendar_name(), create_calendar_opts()) ::
  {:ok, calendar()} | {:error, :no_user | :already_exists | :invalid}
```

# `delete_calendar`

```elixir
@callback delete_calendar(username(), calendar_name()) :: :ok | {:error, :not_found}
```

# `delete_object`

```elixir
@callback delete_object(username(), calendar_name(), object_name()) ::
  :ok | {:error, :not_found | :precondition_failed}
```

# `get_calendar`

```elixir
@callback get_calendar(username(), calendar_name()) :: calendar() | nil
```

# `get_calendar_with_objects`

```elixir
@callback get_calendar_with_objects(username(), calendar_name()) :: calendar() | nil
```

# `get_object`

```elixir
@callback get_object(username(), calendar_name(), object_name()) :: object() | nil
```

# `list_calendars`

```elixir
@callback list_calendars(username()) :: [calendar()]
```

# `put_object`

```elixir
@callback put_object(username(), calendar_name(), object_name(), ical :: String.t()) ::
  {:ok, object()} | {:error, :not_found | :invalid}
```

# `sync_changes`

```elixir
@callback sync_changes(username(), calendar_name(), since :: non_neg_integer() | nil) ::
  {[object()], [object_name()], non_neg_integer()} | {:error, :not_found}
```

# `update_calendar`

```elixir
@callback update_calendar(username(), calendar_name(), update_props()) ::
  {:ok, calendar()} | {:error, :not_found | :invalid}
```

# `user_exists?`

```elixir
@callback user_exists?(username()) :: boolean()
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
