# `Image.Plug.Provider.Cloudinary.URL`
[🔗](https://github.com/elixir-image/image_plug/blob/v0.1.0/lib/image/plug/provider/cloudinary/url.ex#L1)

URL-shape recognition for the [Cloudinary delivery URL grammar](https://cloudinary.com/documentation/transformation_reference).

Cloudinary URLs have the shape:

    <host>/<account>/<resource-type>/<delivery>/[<signature>/]<transforms>/<source>

with:

* `<account>` — the cloud name (e.g. `demo`).
* `<resource-type>` — `image`, `video`, `raw`. v0.1 supports `image`.
* `<delivery>` — `upload`, `fetch`, `private`, `authenticated`, `sprite`,
  `facebook`, etc. v0.1 recognises any value but only `upload` and
  `fetch` are exercised by tests.
* `<signature>` — optional `s--<hex>--` segment for signed URLs.
* `<transforms>` — zero or more comma-separated transform stages,
  each separated by `/`. v0.1 collapses multi-stage transforms by
  concatenating with commas; the canonical IR doesn't model chained
  transforms as v0.1.
* `<source>` — the public id (with extension); for `delivery=fetch`
  it's a percent-encoded HTTPS URL.

Unlike imgix and Cloudflare, Cloudinary embeds account + delivery
segments in the path, so the recogniser strips them before reaching
the source.

# `recognised`

```elixir
@type recognised() :: %{
  shape: :cloudinary,
  options: String.t(),
  source: Image.Plug.Source.t(),
  account: String.t() | nil,
  resource_type: String.t(),
  delivery: String.t(),
  signature: String.t() | nil
}
```

The recognised URL shape.

* `:options` is the joined transform string (multiple stages comma-
  flattened) and may be empty.

* `:source` is the resolved source.

* `:account`, `:resource_type`, `:delivery`, `:signature` are the
  parsed path segments. Used by the wiring module to verify
  signatures and to reconstruct the canonical-string for HMAC
  verification.

# `parse`

```elixir
@spec parse(
  Plug.Conn.t(),
  keyword()
) :: {:ok, recognised()} | {:error, Image.Plug.Error.t()}
```

Parses the request path of a `Plug.Conn` into a recognised URL shape.

### Arguments

* `conn` is a `Plug.Conn` struct.

* `options` is a keyword list. The following keys are honoured:

### Options

* `:mount` — string path prefix the plug is mounted under. Stripped
  before treating the rest as the cloudinary path. Defaults to `""`.

* `:account` — when set, asserts the URL's account segment matches
  this value. When `nil` (default), any account is accepted and
  the parsed value is reported in the recognised shape.

### Returns

* `{:ok, recognised}` on a successful match.

* `{:error, %Image.Plug.Error{tag: :malformed_url}}` when the path
  doesn't sit under the mount or has too few segments.

### Examples

    iex> conn = %Plug.Conn{
    ...>   path_info: ["demo", "image", "upload", "w_200,c_fill", "sample.jpg"],
    ...>   request_path: "/demo/image/upload/w_200,c_fill/sample.jpg",
    ...>   query_string: ""
    ...> }
    iex> {:ok, parsed} = Image.Plug.Provider.Cloudinary.URL.parse(conn, [])
    iex> parsed.options
    "w_200,c_fill"
    iex> parsed.delivery
    "upload"
    iex> parsed.source.ref
    "/sample.jpg"

---

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