# Response

> The server sends exactly one response, whose payload is exactly the byte 0 when the image no longer exists, exactly the byte 1 when a running container runs it and it was not deleted, exactly the byte 2 when the server keeps no client image, or the byte 3 followed by an error as one JSON value, and the response finish follows it.

Canonical: https://protocol.diverge.network/2.3.0/endpoints/images-delete/response/
Specification revision: 2.3.0

The server sends exactly one response on the scope. The response
finish follows the response, and no frame follows the finish.

```text
[0]                  deleted
[1]                  not deleted: a running container runs the image
[2]                  not deleted: the server keeps no client image
[3][error JSON …]    not deleted
```

- **The sequence.** Exactly one response precedes the response
  finish, and nothing follows the finish. A response finish that no
  response precedes states that the request was not served, as
  [Endpoints](/2.3.0/endpoints/) provides; whether the image exists is then
  not stated, and the client learns it from an
  [images::list](/2.3.0/endpoints/images-list/) scope.
- **Deleted.** A payload whose first byte is `0` states that the
  image no longer exists. The server sends it after the image is
  gone. From the moment the server sends it, every `images::list`
  scope the client opens omits the image, and every list scope the
  client holds open that was sent the image as added is sent it as
  removed. The server sends nothing after the byte `0`, and a client
  ignores every byte that follows it.
- **In use.** A payload whose first byte is `1` states that a running
  container of the client runs the image at the time of the request,
  and that it was not deleted. The image and the client's listing are
  as they were before the request. The server sends nothing after the
  byte `1`, and a client ignores every byte that follows it.
- **Unsupported.** A payload that is the byte `2` and nothing else
  states that the server holds out no
  [`images_store`](/2.3.0/capabilities/#images_store) capability and keeps
  no client image, as [Capabilities](/2.3.0/capabilities/) provides. It is
  the only response of the scope, sent before anything is looked up;
  bytes after it are not read.
- **The error.** A payload whose first byte is `3` carries an error
  after that byte, running to the end of the payload. The error is
  exactly one JSON value, in the form defined on the
  [volumes::list response](/2.3.0/endpoints/volumes-list/response/) page.
  This revision prescribes nothing about its content. The error
  states that the image exists as a result of the request, as it did
  before it, and that the reason is not a running container.
- **Malformed.** A payload with no byte, a payload whose first byte
  is none of `0`, `1`, `2` and `3`, and a payload whose bytes after
  `3` are not one JSON value are malformed.

The response is defined by `diverge-sdk-rs/src/provider/endpoints/images/delete/server/response/frame.rs`:

```rust
//! What a server's response frame carries for an image deletion.

use std::fmt;

use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
use crate::shared::error::Error;

/// The image is gone, or a running container runs it and it stays,
/// or it is not gone for some other reason.
///
/// One of these on channel `0`, then the scope finishes. A payload
/// leads with one byte saying which — `0` for
/// [`Deleted`](Self::Deleted), `1` for [`InUse`](Self::InUse), `2`
/// for [`Unsupported`](Self::Unsupported), `3` for
/// [`Error`](Self::Error) — and for the first three there is nothing
/// after it, because saying so IS the whole message.
///
/// | the scope ends with | means |
/// |----------------------|-------|
/// | [`Deleted`](Self::Deleted), then a finish | the image is gone and a listing will not show it |
/// | [`InUse`](Self::InUse), then a finish | a running container of the caller runs it, and nothing was changed |
/// | [`Unsupported`](Self::Unsupported), then a finish | the provider has no image store, and there was nothing to delete |
/// | an [`Error`](Self::Error), then a finish | it is not gone, for some other reason, and a caller should assume it is intact |
///
/// # In use is an answer, not an error
///
/// A provider does not delete an image a running container of the
/// caller runs, and it says so with [`InUse`](Self::InUse) rather than
/// with an [`Error`](Self::Error) a caller could not tell from any
/// other failure. The distinction is what a caller acts on: an image
/// in use is one to stop the container over and ask again, and a
/// failure is not.
///
/// # Gone means gone
///
/// The image no longer exists, and the room it took is the store's
/// again. A caller that wants the name back with new content deletes
/// and builds — two asks, because they are two things.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
    /// The image is gone. Tag `0`.
    Deleted,
    /// A running container of the caller runs the image, and it was
    /// not deleted. Tag `1`.
    ///
    /// Nothing was changed: the image and the listing are as they
    /// were.
    InUse,
    /// The provider has no image store — its `images_store` is `false`,
    /// see [`info`](crate::provider::endpoints::info) — and there was
    /// nothing to delete. Tag `2`.
    ///
    /// Carries nothing. A caller asks another provider; nothing about
    /// the request is the cause.
    Unsupported,
    /// A failure. Tag `3`.
    ///
    /// See [`shared::error::Error`](crate::shared::error::Error) for
    /// why it says so little.
    Error(Error),
}

/// Tag for [`Frame::Deleted`].
const DELETED: u8 = 0;

/// Tag for [`Frame::InUse`].
const IN_USE: u8 = 1;

/// Tag for [`Frame::Unsupported`].
const UNSUPPORTED: u8 = 2;

/// Tag for [`Frame::Error`].
const ERROR: u8 = 3;

/// A tag, and for a failure the JSON after it: an
/// [`Error`](Frame::Error) is a [`serde_json::Value`], which cannot
/// come back out of a format with no self-description, and the other
/// two are the tag alone.
impl Encode for Frame {
    /// The ordinary JSON failure, from the one variant that has one.
    /// A lone tag byte cannot fail.
    type Error = serde_json::Error;

    // Spelled out rather than `Self::Error`: this enum has a variant
    // called `Error`, so the associated type is ambiguous by that name.
    fn encode(&self, out: &mut Writer<'_>) -> Result<(), serde_json::Error> {
        match self {
            Frame::Deleted => {
                out.extend_from_slice(&[DELETED]);
                Ok(())
            }
            Frame::InUse => {
                out.extend_from_slice(&[IN_USE]);
                Ok(())
            }
            Frame::Unsupported => {
                out.extend_from_slice(&[UNSUPPORTED]);
                Ok(())
            }
            Frame::Error(error) => {
                out.extend_from_slice(&[ERROR]);
                error.encode(out)
            }
        }
    }
}

impl Decode<'_> for Frame {
    /// Three ways to fail, and only one of them is a parse.
    type Error = FrameError;

    // Spelled out for the same reason as `encode` above.
    fn decode(bytes: &[u8]) -> Result<Self, FrameError> {
        let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
        match *tag {
            DELETED => Ok(Frame::Deleted),
            IN_USE => Ok(Frame::InUse),
            UNSUPPORTED => Ok(Frame::Unsupported),
            ERROR => Error::decode(rest).map(Frame::Error).map_err(FrameError::Error),
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}

/// An image deletion result that could not be read.
#[derive(Debug)]
pub enum FrameError {
    /// No bytes at all, so not even a tag.
    Empty,
    /// A tag that is none of this frame's four.
    UnknownTag(u8),
    /// The error did not parse.
    Error(serde_json::Error),
}

impl fmt::Display for FrameError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FrameError::Empty => f.write_str("image deletion result frame is empty"),
            FrameError::UnknownTag(tag) => write!(f, "unknown image deletion result frame tag {tag}"),
            FrameError::Error(error) => write!(f, "image deletion error did not parse: {error}"),
        }
    }
}

impl std::error::Error for FrameError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            FrameError::Error(error) => Some(error),
            FrameError::Empty | FrameError::UnknownTag(_) => None,
        }
    }
}
```
