# Response

> The server sends exactly one response: the byte 0 alone, Serving, after which the scope stays open until the client stops it; the byte 1, 2 or 3 alone when a running container, another FUSE serve or an operation holds the volume; the byte 4 followed by the volume and the mode it is in as JSON, when the request meant another; the byte 5 alone when the server does not FUSE serve a volume of its mode, or keeps no volume; or the byte 6 followed by an error as one JSON value; every refusal and an error are followed by the response finish.

Canonical: https://protocol.diverge.network/2.3.0/endpoints/volumes-fuse-serve/response/
Specification revision: 2.3.0

The server sends exactly one response on the scope, and sends it
before it serves any channel.

```text
[0]                      serving: the volume is held, and the asks are answered
[1]                      not served: a running container holds the volume
[2]                      not served: another FUSE serve holds the volume
[3]                      not served: an operation holds the volume
[4][volume-mode JSON …]  not served: the volume is not in the mode the request means
[5]                      not served: the server does not FUSE serve a volume of its mode, or keeps no volume
[6][error JSON …]        not served
```

- **The sequence.** Exactly one response, first. After a response
  whose payload is exactly the one byte `0`, the scope stays open:
  the server sends no other response on the scope, answers every
  channel the client opens as [Client Channels](/2.3.0/endpoints/volumes-fuse-serve/client/) states,
  and sends the response finish after the last answer when the client
  stops. After a response whose first byte is `1` to `6`, the
  response finish follows the response, and no frame follows the
  finish. A response finish that no response precedes states that
  the request was not served, as [Endpoints](/2.3.0/endpoints/) provides.
- **The refusals, and their order.** The server decides the bytes
  `1` to `5` for the volume the request names in the order
  [Capabilities](/2.3.0/capabilities/#the-hold) states for a FUSE serve,
  the first rule that applies deciding; on a server that keeps no
  volume at all, the byte `5` is the only response, sent before
  anything is looked up. Every refusal states that the volume is not
  served, that nothing is held, and that nothing changed.
- **Mounted.** A payload that is the byte `1` and nothing else states
  that a running container of the client holds the volume at the
  time of the request, on a server whose
  [mode object](/2.3.0/capabilities/#the-mode-object) for the volume's
  mode has `mount_and_fuse_serve` `false`.
- **FUSE served.** A payload that is the byte `2` and nothing else
  states that another FUSE serve of the client holds the volume at
  the time of the request, on a server whose mode object for the
  volume's mode has `fuse_serve_many` `false`.
- **Held.** A payload that is the byte `3` and nothing else states
  that the volume is under a [volumes::stat](/2.3.0/endpoints/volumes-stat/),
  [volumes::read](/2.3.0/endpoints/volumes-read/),
  [volumes::write](/2.3.0/endpoints/volumes-write/),
  [volumes::filetree](/2.3.0/endpoints/volumes-filetree/),
  [volumes::edit](/2.3.0/endpoints/volumes-edit/) or
  [volumes::delete](/2.3.0/endpoints/volumes-delete/) at the time of the
  request.
- **Volume mode.** A payload whose first byte is `4` carries, after
  that byte as JSON, an object with the members `name`, a string, and
  `mode`, a string: the name the request gave, and the mode the
  volume is in, as the client's listing reports it, one of
  `persistent`, `ephemeral` and `read_only`. The server sends it when
  the request's `mode` is not the volume's; the volume is not served
  and is as it was.
- **Unsupported.** A payload that is the byte `5` and nothing else
  states that the server holds out no mode object for the volume's
  mode, or one whose `fuse_serve` is `false`; or that the server
  holds out none of the three mode objects and keeps no volume, as
  [Capabilities](/2.3.0/capabilities/#the-volume-modes) provides.
- **The error.** A payload whose first byte is `6` 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.
- **Malformed.** A payload with no byte, a payload whose first byte
  is none of `0` to `6`, a payload whose bytes after `4` do not parse
  as the refusal, and a payload whose bytes after `6` are not one
  JSON value are malformed. A payload whose first byte is `0`, `1`,
  `2`, `3` or `5` is read by that byte alone; bytes after it are not
  read.

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

```rust
//! What a server's response frame carries for a volume FUSE serve.

use std::fmt;

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

/// The volume is served, or the news that it will not be.
///
/// A payload leads with one byte saying which — `0` for
/// [`Serving`](Self::Serving), `1` for [`Mounted`](Self::Mounted),
/// `2` for [`FuseServed`](Self::FuseServed), `3` for
/// [`Held`](Self::Held), `4` for [`VolumeMode`](Self::VolumeMode),
/// `5` for [`Unsupported`](Self::Unsupported), `6` for
/// [`Error`](Self::Error) — and the rest is that variant's own JSON,
/// of which only the mode refusal and the error have any.
///
/// # How the scope runs
///
/// | the scope | means |
/// |-----------|-------|
/// | a serving, then quiet, and stays open | the volume is held, and every ask the caller opens is answered on its channel |
/// | a serving, then a finish | the caller stopped, and the volume is given back |
/// | a mounted, then a finish | it was not served: a running container has the volume, and the provider does not FUSE serve a volume of its mode beside one |
/// | a FUSE served, then a finish | it was not served: another FUSE serve holds the volume, and the provider FUSE serves a volume of its mode once |
/// | a held, then a finish | it was not served: another operation has the volume to itself |
/// | a volume mode, then a finish | it was not served: the volume is not in the mode the request means |
/// | an unsupported, then a finish | it was not served: the provider does not FUSE serve a volume of its mode |
/// | an error, then a finish | it was not served: no volume of the caller's has the name |
///
/// Exactly one response, first. Nothing else comes on channel `0`;
/// the answers to asks come on the asks' channels.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
    /// The volume is held, and asks may be opened. Tag `0`.
    Serving,
    /// A running container has the volume, and the provider does not
    /// FUSE serve a volume of its mode beside one — its
    /// `mount_and_fuse_serve` for the mode is `false`. Tag `1`.
    ///
    /// Nothing is held and nothing changed; a caller stops the
    /// container, or asks another provider, and asks again.
    Mounted,
    /// Another FUSE serve holds the volume, and the provider FUSE
    /// serves a volume of its mode once — its `fuse_serve_many` for
    /// the mode is `false`. Tag `2`.
    ///
    /// Nothing is held and nothing changed; a caller stops that FUSE
    /// serve, or asks another provider, and asks again.
    FuseServed,
    /// Another operation — a stat, a read, a write, a filetree, an
    /// edit or a delete — has the volume to itself. Tag `3`.
    ///
    /// Nothing is held and nothing changed; a caller waits for it to
    /// end and asks again.
    Held,
    /// The volume is not in the mode the request means it to have.
    /// Tag `4`.
    ///
    /// Nothing is held and nothing changed; the volume is in the mode
    /// reported. See [`VolumeMode`].
    VolumeMode(VolumeMode),
    /// The provider does not FUSE serve a volume of the mode the
    /// volume is in — its `fuse_serve` for the mode is `false`, see
    /// [`info`](crate::provider::endpoints::info) — or keeps no
    /// volumes at all, no mode being among its capabilities, which
    /// every volume endpoint answers so. Tag `5`.
    ///
    /// Nothing is held and nothing changed; a caller asks another
    /// provider, or has the volume edited into a mode this one
    /// serves.
    Unsupported,
    /// A failure. Tag `6`.
    ///
    /// See [`shared::error::Error`](crate::shared::error::Error) for
    /// why it says so little.
    Error(Error),
}

/// Tag for [`Frame::Serving`].
const SERVING: u8 = 0;

/// Tag for [`Frame::Mounted`].
const MOUNTED: u8 = 1;

/// Tag for [`Frame::FuseServed`].
const FUSE_SERVED: u8 = 2;

/// Tag for [`Frame::Held`].
const HELD: u8 = 3;

/// Tag for [`Frame::VolumeMode`].
const VOLUME_MODE: u8 = 4;

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

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

impl Encode for Frame {
    /// The ordinary JSON failure, from the half that has one.
    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::Serving => {
                out.extend_from_slice(&[SERVING]);
                Ok(())
            }
            Frame::Mounted => {
                out.extend_from_slice(&[MOUNTED]);
                Ok(())
            }
            Frame::FuseServed => {
                out.extend_from_slice(&[FUSE_SERVED]);
                Ok(())
            }
            Frame::Held => {
                out.extend_from_slice(&[HELD]);
                Ok(())
            }
            Frame::VolumeMode(refused) => {
                out.extend_from_slice(&[VOLUME_MODE]);
                serde_json::to_writer(out, refused)
            }
            Frame::Unsupported => {
                out.extend_from_slice(&[UNSUPPORTED]);
                Ok(())
            }
            Frame::Error(error) => {
                out.extend_from_slice(&[ERROR]);
                error.encode(out)
            }
        }
    }
}

impl Decode<'_> for Frame {
    /// Four ways to fail, and two of them are parses.
    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 {
            SERVING => Ok(Frame::Serving),
            MOUNTED => Ok(Frame::Mounted),
            FUSE_SERVED => Ok(Frame::FuseServed),
            HELD => Ok(Frame::Held),
            VOLUME_MODE => serde_json::from_slice(rest).map(Frame::VolumeMode).map_err(FrameError::VolumeMode),
            UNSUPPORTED => Ok(Frame::Unsupported),
            ERROR => Error::decode(rest).map(Frame::Error).map_err(FrameError::Error),
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}

/// A volume FUSE serve response frame 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 seven.
    UnknownTag(u8),
    /// The mode refusal did not parse.
    VolumeMode(serde_json::Error),
    /// 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("volume FUSE serve response frame is empty"),
            FrameError::UnknownTag(tag) => write!(f, "unknown volume FUSE serve response frame tag {tag}"),
            FrameError::VolumeMode(error) => write!(f, "volume FUSE serve mode refusal did not parse: {error}"),
            FrameError::Error(error) => write!(f, "volume FUSE serve error did not parse: {error}"),
        }
    }
}

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

and the volume mode by `diverge-sdk-rs/src/shared/containers/response/volume_mode.rs`:

```rust
//! A run, or a FUSE serve, refused because a volume it names is not in
//! the mode it meant.

use serde::{Deserialize, Serialize};

use crate::provider::endpoints::volumes::Mode;

/// The volume a request named under one mode that is in another, and
/// so refused the request: a run, whose every mount states the mode it
/// means the volume to have, or a FUSE serve, whose request does.
///
/// Nothing changed: the volume is in the mode reported here, as its
/// listing reports it, and only an
/// [`edit`](crate::provider::endpoints::volumes::edit) changes it. The
/// provider answers this after it has held the volume and given the
/// hold back, so that an edit cannot land between the look and the
/// answer — the first and only response of its scope, then the
/// finish — with nothing fetched and nothing deployed for it. The
/// mode the request meant is the request's to read back.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct VolumeMode {
    /// The `volume_name` of the volume, as the request named it.
    pub name: String,
    /// The mode the volume is in.
    pub mode: Mode,
}
```
