# `Licet.Relationship`
[🔗](https://github.com/netoum/licet/blob/v0.1.0/lib/licet/relationship.ex#L1)

A relationship: one stored fact linking a resource to a subject.

Licet functions accept relationships as text, in SpiceDB's format, or as
this struct. `Licet.read/3` returns structs.

| Text | Meaning |
| --- | --- |
| `document:roadmap#owner@user:anne` | Anne is an owner of the roadmap |
| `document:roadmap#viewer@team:eng#member` | members of team eng are viewers |
| `document:roadmap#viewer@user:*` | every user is a viewer |
| `document:roadmap#viewer@user:anne[expiration:2027-01-01T00:00:00Z]` | Anne is a viewer until 2027 |

A subject written `user:anne#...` is the same as `user:anne`. In the struct,
`subject_relation` is `""` when the subject has no relation.

Relation names are lowercase, 3 to 64 characters. Type names follow the same
rule, and may be namespaced as `prefix/name`, up to 128 characters in total,
as in SpiceDB. Ids are 1 to 1024
characters from `A-Z`, `a-z`, `0-9`, and `/_|=-+`, or `*` for a wildcard
subject. Names stay strings; Licet never turns them into atoms.

# `t`

```elixir
@type t() :: %Licet.Relationship{
  expires_at: DateTime.t() | nil,
  relation: String.t(),
  resource_id: String.t(),
  resource_type: String.t(),
  subject_id: String.t(),
  subject_relation: String.t(),
  subject_type: String.t()
}
```

# `parse`

```elixir
@spec parse(String.t()) :: {:ok, t()} | {:error, Licet.Error.t()}
```

Parses relationship text.

This checks the format only. Whether the types and relations exist in the
schema is checked when the relationship is written.

## Examples

    iex> {:ok, rel} = Licet.Relationship.parse("document:1#viewer@team:eng#member")
    iex> {rel.resource_type, rel.resource_id, rel.relation}
    {"document", "1", "viewer"}
    iex> {rel.subject_type, rel.subject_id, rel.subject_relation}
    {"team", "eng", "member"}

    iex> {:ok, rel} = Licet.Relationship.parse("document:1#viewer@user:ada[expiration:2027-01-01T00:00:00Z]")
    iex> rel.expires_at
    ~U[2027-01-01 00:00:00Z]

    iex> {:error, %Licet.Error{reason: reason}} = Licet.Relationship.parse("document:1")
    iex> reason
    :invalid_relationship

# `parse_object`

```elixir
@spec parse_object(String.t()) ::
  {:ok, String.t(), String.t(), String.t()} | {:error, Licet.Error.t()}
```

Parses an object, such as `"document:1"`, or a userset, such as
`"team:eng#member"`, into its type, id, and relation.

The relation is `""` for a plain object.

## Examples

    iex> Licet.Relationship.parse_object("document:1")
    {:ok, "document", "1", ""}

    iex> Licet.Relationship.parse_object("team:eng#member")
    {:ok, "team", "eng", "member"}

# `to_string`

```elixir
@spec to_string(t()) :: String.t()
```

Renders a relationship as text, the inverse of `parse/1`.

## Examples

    iex> {:ok, rel} = Licet.Relationship.parse("document:1#viewer@team:eng#member")
    iex> Licet.Relationship.to_string(rel)
    "document:1#viewer@team:eng#member"

---

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