# Defining Views

A view holds the data that queries read.

Define the shape of an item as a struct that derives `Clone`, `Debug`, `Default`, and `PartialEq`, and create an `InMemoryView` with a closure that returns the key of an item, to get a view that holds such items in memory:

```rust
#[derive(Clone, Debug, Default, PartialEq)]
struct BookItem {
  id: String,
  title: String,
  author: String,
  is_borrowed: bool,
  borrowed_until: String,
  revision: Option<Revision>,
}

fn new_catalog() -> InMemoryView<String, BookItem> {
  InMemoryView::with_revision_in(|item: &BookItem| item.id.clone(), |item| &mut item.revision)
}

let catalog = new_catalog();
```

An item derives neither `Serialize` nor `Deserialize`, since what a caller sees is decided by a query and its answer, not by the view (see **[Handling Queries over HTTP](/docs/architecturekit/rust/handling-queries-over-http)**). It derives `Clone`, since the view hands out clones of its items (see **[Sharing Items with Readers](/docs/architecturekit/rust/defining-views#sharing-items-with-readers)**), `Default`, which `upsert` starts from (see **[Adding Items](/docs/architecturekit/rust/defining-views#adding-items)**), and `Debug` and `PartialEq`, so that a test can compare the items of the view with the ones it expects (see **[Testing Projections](/docs/architecturekit/rust/testing-projections)**).

Every item has a revision of its own, which is the ID of the last event that changed it. `with_revision_in` takes a second closure, which returns a field of the item of the type `Option<Revision>`, and makes the view keep the revision there, so that a caller can hand it over to a command that uses `Precondition::on_event_id` (see **[Checking the Revision of the Caller](/docs/architecturekit/rust/using-preconditions#checking-the-revision-of-the-caller)**). **The view sets the field whenever it changes an item, so you never set it yourself.** A view created with `InMemoryView::new`, which takes the first closure only, keeps the revisions to itself.

The revision of an item fits such a precondition only if the item stands for exactly one subject, and the projection applies every event type of that subject to the item. The precondition checks the last event of the subject, so as soon as an event lands in the subject that the view does not apply to the item, the two drift apart, and every command with the revision of the item fails with a conflict, until an event changes the item again. Apply an event type that does not change the item, such as one whose arm in `evolve` returns the state unchanged, with a change that does nothing (see **[Defining Projections](/docs/architecturekit/rust/projections#defining-projections)**). For an item that gathers several subjects, such as all books a reader has borrowed, there is no single subject its revision could stand for, so use `Precondition::on_state_read` for the commands instead (see **[Guarding Against Concurrent Changes](/docs/architecturekit/rust/using-preconditions#guarding-against-concurrent-changes)**).

Every method that changes the view takes the event it applies, as the `Envelope` that a projection gets (see **[Defining Projections](/docs/architecturekit/rust/projections#defining-projections)**). The ID of an event is a `Revision`, so the view compares IDs as numbers: event `10` is newer than event `9`. **An event that is not newer than the item it is about is skipped, so applying the same event twice changes nothing**, as long as the item is still there. The view forgets the revision of an item it removes, so an event that adds the item, applied again after a later event has removed it, adds it again. A projection that is rebuilt applies every event once, in order, so with a view in memory, that does not happen.

Reading a view is asynchronous and may fail, as it does for a view in a database: it goes through the traits `View` and `KeyedView`, whose methods hand out a `Result`, so that code that reads a view works the same, whatever is behind it. The view in memory never fails, so its error is `Infallible`, and a reader needs no handling for it. Changing a view, on the other hand, goes through the methods of `InMemoryView` itself, which take effect at once. The kit has no trait for the methods that change a view, though: a projection holds its view by its type, such as `InMemoryView`, so moving it to a view in a database changes that type.

If an upcaster splits a stored event into several events, they all carry the ID of the stored event (see **[Versioning Events](/docs/architecturekit/rust/versioning-events)**), and each its position among them, as the `part` field of the envelope. For the same ID, the view counts a later part as newer than an earlier one, so every one of them is applied, in order, also if several of them change the same item. Applying the stored event again still changes nothing, and the revision of the item stays the ID of the stored event. That is why a projection hands the envelope it gets on to the view, rather than one it builds itself.

*Note that the view as a whole has a revision as well, which is the last event it has seen at all, rather than the last one that changed a particular item (see **[Reading Your Own Writes](/docs/architecturekit/rust/reading-your-own-writes)**).*

## Adding Items

To add an item, call the `insert` method with the event and the item:

```rust
let outcome = catalog.insert(
  &event,
  BookItem {
    id: "42".to_owned(),
    title: "2001 – A Space Odyssey".to_owned(),
    author: "Arthur C. Clarke".to_owned(),
    ..BookItem::default()
  },
)?;
```

`insert` returns an `Outcome`, which tells what it did: `Outcome::Added` if it added the item, and `Outcome::AlreadyApplied` if an item with the same key has seen the event, or a newer one, so the event was skipped. **The latter is not an error**, since it happens when events are applied a second time. So a projection that does more than store the item, for example one that also counts the books, does so only for `Outcome::Added` (see **[Changing and Removing Items](/docs/architecturekit/rust/defining-views#changing-and-removing-items)**).

If the key of the item is already taken, and the event is newer than the item with that key, `insert` fails with `ViewError`, which belongs to `Category::Permanent`, since two items with the same key point to a mistake in the events or in the key. To add an item or change the existing one, call the `upsert` method instead, with the key, the event, and a closure that changes the item. It starts from the existing item, or from the `Default` of the item if there is none, so it has to set the fields that make up the key:

```rust
let outcome = catalog.upsert("42", &event, |item| {
  item.id = "42".to_owned();
  item.is_borrowed = true;
})?;
```

`upsert` returns an `Outcome` as well: `Outcome::Added` if it added the item, `Outcome::Applied` if it changed the existing one, and `Outcome::AlreadyApplied` if the existing item has seen the event, or a newer one, so the event was skipped.

*Note that an item whose key, after the change, differs from the given one fails with `ViewError`, which also catches a closure that forgets to set the key.*

## Reading Items

To read the item with a given key, call the `get` method of the `KeyedView` trait. It returns `None` if there is none:

```rust
let Ok(book) = catalog.get("42").await;
```

To read all items, call the `all` method of the `View` trait. It returns a stream of clones of the items, taken when `all` is called, in the order in which they were added, which hands out each item as a `Result`. To go through it, pin it with `std::pin::pin!`, and call the `next` method of `StreamExt` in a loop, or hand it to the steps of a query (see **[Defining Queries](/docs/architecturekit/rust/defining-queries)**). `StreamExt` comes from the **[futures](https://crates.io/crates/futures)** crate, which you add with `cargo add futures`:

```rust
let mut items = pin!(catalog.all());

while let Some(item) = items.next().await {
  let Ok(item) = item;

  // ...
}
```

The view in memory never fails while it hands out its items, so its error is `Infallible`, and `let Ok(item) = item;` needs no other arm. A view in a database, however, may fail halfway, which is why every view hands out a `Result`, and its stream ends after an error.

*Note that the items you get are clones, which a later change of the view does not reach, and that taking them when `all` is called lets a query take its time without holding up the projection that feeds the view (see **[Sharing Items with Readers](/docs/architecturekit/rust/defining-views#sharing-items-with-readers)**).*

## Changing and Removing Items

To change the item with a given key, call the `update` method with the event and a closure that changes it. To remove it, call the `delete` method. Like `insert` and `upsert`, both return an `Outcome`, which tells what they did. Removing never fails, so `delete` returns the `Outcome` alone:

```rust
let updated = catalog.update("42", &event, |item| {
  item.is_borrowed = true;
})?;

let deleted = catalog.delete("42", &event);
```

| Outcome | Meaning |
|---|---|
| `Outcome::Added` | A new item was added. Only `insert` and `upsert` report it. |
| `Outcome::Applied` | An existing item was changed or removed. |
| `Outcome::Missing` | There is no item with the key. Only `update` and `delete` report it. |
| `Outcome::AlreadyApplied` | The item has seen the event, or a newer one, so nothing was changed. |

Neither `Outcome::Missing` nor `Outcome::AlreadyApplied` is an error, since both happen when events are applied a second time, as a later event may have removed the item already. If an event about an item that does not exist means that the view and the database disagree, say so in the projection, with an error type of its own that belongs to `Category::Permanent`:

```rust
#[derive(Debug)]
enum CatalogError {
  View(ViewError),
  NeverAcquired(String),
}

impl From<ViewError> for CatalogError {
  fn from(error: ViewError) -> Self {
    Self::View(error)
  }
}

impl fmt::Display for CatalogError {
  fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    match self {
      Self::View(error) => error.fmt(formatter),
      Self::NeverAcquired(book_id) => write!(formatter, "book {book_id} was never acquired"),
    }
  }
}

impl std::error::Error for CatalogError {}

impl Categorized for CatalogError {
  fn category(&self) -> Option<Category> {
    Some(Category::Permanent)
  }
}
```

The projection then refuses the event, and the run names the event in the text of its failure, as in `applying event 7: book 42 was never acquired` (see **[Running Projections](/docs/architecturekit/rust/projections#running-projections)**):

```rust
let outcome = catalog.update(book_id, &event, |item| {
  item.is_borrowed = true;
})?;
if outcome == Outcome::Missing {
  return Err(CatalogError::NeverAcquired(book_id.to_owned()));
}
```

*Note that changing the key of an item fails with `ViewError`, and leaves the item as it was.*

To change or remove several items at once, call the `update_where` or the `delete_where` method with a closure that selects them. Both return the number of items they changed or removed:

```rust
let is_by_clarke = |item: &BookItem| item.author == "Arthur C. Clarke";

let changed = catalog.update_where(is_by_clarke, &event, |item| {
  item.author = "Sir Arthur C. Clarke".to_owned();
})?;

let removed = catalog.delete_where(is_by_clarke, &event);
```

*Note that the view stays locked while it runs a closure you hand over, such as a change, or the closure that selects the items for `update_where` and `delete_where`. Such a closure must not use the same view, not even to read an item with `get` or `all`, since that blocks forever. The same holds for `upsert`, and for the `update` method of an index. To copy data from another item of the view into the one you change, read it before, and use it inside the change. A panic in such a closure leaves the view usable for those who read it.*

## Sharing Items with Readers

`get` and `all` hand out clones of the items, so **every reader holds a copy of its own**, which a later change does not reach, and which it may change as it likes without changing the view. A change, in turn, works on a clone of the item, which replaces the item only once the change is accepted. So a change may write into a field that holds a `Vec` or a `HashMap`, rather than building it anew, as in a view of the readers of the library and the books they have borrowed:

```rust
#[derive(Clone, Default)]
struct ReaderItem {
  id: String,
  book_ids: Vec<String>,
  due_dates: HashMap<String, String>,
}

fn new_readers() -> InMemoryView<String, ReaderItem> {
  InMemoryView::new(|item: &ReaderItem| item.id.clone())
}
```

For a `BookBorrowed` event, whose data is `borrowed`, the projection adds the book to its reader:

```rust
let outcome = readers.update(&borrowed.borrowed_by, &event, |item| {
  item.book_ids.push(book_id.to_owned());
  item.due_dates.insert(book_id.to_owned(), borrowed.borrowed_until.clone());
})?;
```

The view clones an item whenever it hands it out, with `get`, `all`, and the `lookup` method of an index, and whenever it changes one, with `update`, `upsert`, `update_where`, and the `update` method of an index. That is why an item derives `Clone`, which `InMemoryView` requires.

*Note that a change that panics leaves the item as it was, since its clone never replaces the item.*

## Indexing Items

Selecting items with a closure reads every item. To find items by a value directly, for example all books by an author, add a secondary index with the `index` method. It takes a closure that returns the value of an item, and returns an `InMemoryIndex`, which offers the `lookup`, `update`, and `delete` methods:

```rust
let by_author = catalog.index(|item| item.author.clone());

let books = by_author.lookup("Arthur C. Clarke");

let changed = by_author.update("Arthur C. Clarke", &event, |item| {
  item.is_borrowed = false;
})?;

let removed = by_author.delete("Arthur C. Clarke", &event);
```

Several items may share a value, so `update` and `delete` return the number of items they changed or removed, as `update_where` and `delete_where` do. The index follows every change to the view, also when the value of an item changes, and `lookup` hands out the items in the order in which they were added, as a stream of `Result`s, as `all` does.

*Note that adding an index reads every item, so add indexes before the view is used, and that its closure runs while the view is locked, so it must not use the view either.*

To keep items somewhere else, for example in a database, implement the `View` trait, whose `all` method returns a stream of items and errors. Its error type implements `Categorized`, like every error the kit hands on, so that its category decides what happens with it, for example a failure that may pass, such as a lost connection, as `Category::Transient`. With the **[sqlx](https://crates.io/crates/sqlx)** crate and PostgreSQL, which you add with `cargo add sqlx --features postgres,runtime-tokio`, the view and its error look like this:

```rust
struct BookTable {
  pool: PgPool,
}

#[derive(Debug)]
struct BookTableError(sqlx::Error);

impl fmt::Display for BookTableError {
  fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(formatter, "book table: {}", self.0)
  }
}

impl std::error::Error for BookTableError {
  fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
    Some(&self.0)
  }
}

impl Categorized for BookTableError {
  fn category(&self) -> Option<Category> {
    match self.0 {
      // The connection broke, or no connection was free in time, which may pass.
      sqlx::Error::Io(_) | sqlx::Error::PoolTimedOut => Some(Category::Transient),
      _ => Some(Category::Permanent),
    }
  }
}
```

A database fails not only before it reads the rows, but also while it reads them, for example when the connection breaks halfway, or when a row can not be read. So hand out such an error as an element of the stream, and end the stream there. **Hand on the rows as they come, rather than reading all of them first**, since a caller that has seen enough, for example with `take` or `first`, drops the stream (see **[Defining Queries](/docs/architecturekit/rust/defining-queries)**). The stream of sqlx goes on after a row that can not be read, so `scan` ends it after the first error. `try_get` reads a column of a row, and is a method of the trait `Row` of sqlx, which `use sqlx::Row;` imports:

```rust
impl FromRow<'_, PgRow> for BookItem {
  fn from_row(row: &PgRow) -> Result<Self, sqlx::Error> {
    let revision: Option<String> = row.try_get("revision")?;

    Ok(BookItem {
      id: row.try_get("id")?,
      title: row.try_get("title")?,
      author: row.try_get("author")?,
      is_borrowed: row.try_get("is_borrowed")?,
      borrowed_until: row.try_get("borrowed_until")?,
      revision: revision
        .map(|revision| revision.parse())
        .transpose()
        .map_err(|error| sqlx::Error::Decode(Box::new(error)))?,
    })
  }
}

impl View<BookItem> for BookTable {
  type Error = BookTableError;

  fn all(&self) -> impl Stream<Item = Result<BookItem, BookTableError>> + Send {
    sqlx::query_as("SELECT id, title, author, is_borrowed, borrowed_until, revision FROM books")
      .fetch(&self.pool)
      .map_err(BookTableError)
      // A view ends after its first error.
      .scan(false, |has_failed, item| {
        if *has_failed {
          return future::ready(None);
        }
        *has_failed = item.is_err();

        future::ready(Some(item))
      })
  }
}
```

The error reaches the caller unchanged, through every step of a query, so its category, such as `Category::Transient`, still decides which status code an HTTP route answers with (see **[Mapping Errors to Status Codes](/docs/architecturekit/rust/mapping-errors-to-status-codes)**).

If the view can find a single item by its key, also implement the `KeyedView` trait, whose `get` method returns the item, or `None`:

```rust
impl KeyedView<str, BookItem> for BookTable {
  async fn get(&self, id: &str) -> Result<Option<BookItem>, BookTableError> {
    sqlx::query_as(
      "SELECT id, title, author, is_borrowed, borrowed_until, revision FROM books WHERE id = $1",
    )
    .bind(id)
    .fetch_optional(&self.pool)
    .await
    .map_err(BookTableError)
  }
}
```

A query that reads a single item can then take any view that implements `KeyedView<str, BookItem>` instead of running over all items, and work with the view in memory and the one in a database alike. `InMemoryView` implements `KeyedView` too, for the type of its keys, and for every type a key can be borrowed as, such as `str` for a `String`.
