Response

The server sends the bytes of the file as responses whose first byte is 0, each at most CHUNK_SIZE bytes, and the finish; a file of zero bytes is one empty response; the byte 1, 2 or 3 alone, when a running container, a FUSE serve or an operation holds the volume, or the byte 4 alone, when the server keeps no volume, is the only response; a response whose first byte is 5 carries an error and is the last.

[0][bytes …]         a piece of the file
[1]                  not read: a running container holds the volume
[2]                  not read: a FUSE serve holds the volume
[3]                  not read: another operation holds the volume
[4]                  not read: the server keeps no volume
[5][error JSON …]    the file was not read, or not all of it
  • The sequence. Zero or more responses whose first byte is 0, each a piece of the file in order, followed by the response finish; or the responses sent so far, exactly one response whose first byte is 5, and the response finish; or exactly one response whose payload is the byte 1, 2, 3 or 4 and nothing else, and the response finish. A file of zero bytes is exactly one response whose first byte is 0 and nothing after it. A response finish that no response precedes states that the request was not served, as Endpoints provides.
  • The piece. After the byte 0, the bytes of the piece, verbatim, running to the end of the payload. Each piece is at most CHUNK_SIZE bytes, 2 * 1024 * 1024, the constant diverge-sdk-rs/src/lib.rs defines; the pieces concatenated are the file, and a receiver measures none.
  • The refusals, and their order. The server decides the bytes 1, 2 and 3 in the order Capabilities states for an operation, the first rule that applies deciding, and sends the one that applies before any piece. On a server that keeps no volume, the byte 4 is the only response, sent before anything is looked up. Every refusal states that nothing was read and that nothing changed. A client ignores every byte that follows the byte 1, 2, 3 or 4.
  • 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.
  • FUSE served. A payload that is the byte 2 and nothing else states that a volumes::fuse_serve of the client holds the volume at the time of the request.
  • Held. A payload that is the byte 3 and nothing else states that the volume is under a volumes::stat, another volumes::read, a volumes::write, a volumes::filetree, a volumes::edit or a volumes::delete at the time of the request.
  • Unsupported. A payload that is the byte 4 and nothing else states that the server holds out none of the three mode objects and keeps no volume, as Capabilities provides.
  • The error. A payload whose first byte is 5 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 page. This revision prescribes nothing about its content. The error is the last response of the scope.
  • Malformed. A payload with no byte, a payload whose first byte is none of 0 to 5, and a payload whose bytes after 5 are not one JSON value are malformed.

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

//! What a server's response frame carries for a volume read.

use std::fmt;

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

/// A piece of the file, or the news that there will not be one.
///
/// A payload leads with one byte saying which — `0` for
/// [`Body`](Self::Body), `1` for [`Mounted`](Self::Mounted), `2` for
/// [`FuseServed`](Self::FuseServed), `3` for [`Held`](Self::Held),
/// `4` for [`Unsupported`](Self::Unsupported), `5` for
/// [`Error`](Self::Error) — and the rest is that variant's own bytes,
/// of which the four refusals have none.
///
/// # How it ends
///
/// | the scope ends with | means |
/// |---------------------|-------|
/// | bodies, then a finish | the bytes are the file |
/// | [`Mounted`](Self::Mounted), then a finish | a running container has the volume, and the file was not read |
/// | [`FuseServed`](Self::FuseServed), then a finish | a FUSE serve holds the volume, and the file was not read |
/// | [`Held`](Self::Held), then a finish | another operation has the volume to itself, and the file was not read |
/// | an [`Error`](Self::Error), then a finish | the file was not read, or not all of it |
///
/// Only a finish ends it. A zero-byte file is one empty body, then
/// the finish, so that a finish with nothing before it keeps its
/// standing meaning: could not serve, with nothing to say.
///
/// # Mounted, FUSE served and held are answers, not errors
///
/// A volume that something holds is not touched, and the provider
/// says which thing holds it — a running container, a FUSE serve, or
/// another operation — with its own variant rather than an
/// [`Error`](Self::Error) a caller could not tell from any other
/// failure. The distinction is what a caller acts on: stop the
/// container, stop the FUSE serve, or wait, and ask again.
///
/// # The failure is this scope's, the bytes are not
///
/// [`Body`](Self::Body) carries
/// [`read::response::Frame`](crate::shared::containers::read::response::Frame),
/// which means the same thing wherever the exchange happens — a piece
/// of a file, at most [`CHUNK_SIZE`](crate::CHUNK_SIZE), appended to
/// the pieces before it. The [`Error`](Self::Error) beside it is the
/// exchange's own: a volume the caller cannot see, a path that names
/// no file.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame<'a> {
    /// A piece of the file. Tag `0`.
    ///
    /// Each at most [`CHUNK_SIZE`](crate::CHUNK_SIZE), in order, and
    /// every one appends to the file.
    Body(read::response::Frame<'a>),
    /// The volume is mounted in a running container, and was not
    /// read. Tag `1`.
    ///
    /// Nothing was changed; a caller stops the container and asks
    /// again.
    Mounted,
    /// A FUSE serve holds the volume, and it was not read.
    /// Tag `2`.
    ///
    /// Nothing was changed; a caller stops the FUSE serve and asks
    /// again.
    FuseServed,
    /// Another operation — a stat, a read, a write, a filetree, an
    /// edit or a delete — has the volume to itself, and it was not
    /// read. Tag `3`.
    ///
    /// Nothing was changed; a caller waits for it to end and asks
    /// again.
    Held,
    /// The provider keeps no volumes: no mode is among its capabilities,
    /// see [`info`](crate::provider::endpoints::info), and every volume
    /// endpoint answers this. Tag `4`.
    ///
    /// Carries nothing. Nothing was looked at; a caller asks another
    /// provider.
    Unsupported,
    /// A failure. Tag `5`.
    ///
    /// See [`shared::error::Error`](crate::shared::error::Error) for
    /// why it says so little.
    Error(Error),
}

/// Tag for [`Frame::Body`].
const BODY: 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::Unsupported`].
const UNSUPPORTED: u8 = 4;

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

impl Encode for Frame<'_> {
    /// The ordinary JSON failure, from the half that has one. The
    /// other 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::Body(inner) => {
                out.extend_from_slice(&[BODY]);
                // Its error is `Infallible`, and an empty match on one
                // is how you say so: there is no value to handle.
                inner.encode(out).map_err(|error| match error {})
            }
            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::Unsupported => {
                out.extend_from_slice(&[UNSUPPORTED]);
                Ok(())
            }
            Frame::Error(error) => {
                out.extend_from_slice(&[ERROR]);
                error.encode(out)
            }
        }
    }
}

impl<'a> Decode<'a> for Frame<'a> {
    /// 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: &'a [u8]) -> Result<Self, FrameError> {
        let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
        match *tag {
            BODY => read::response::Frame::decode(rest)
                .map(Frame::Body)
                .map_err(|error| match error {}),
            MOUNTED => Ok(Frame::Mounted),
            FUSE_SERVED => Ok(Frame::FuseServed),
            HELD => Ok(Frame::Held),
            UNSUPPORTED => Ok(Frame::Unsupported),
            ERROR => {
                Error::decode(rest).map(Frame::Error).map_err(FrameError::Error)
            }
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}

/// A volume read response frame that could not be read.
#[derive(Debug)]
pub enum FrameError {
    /// No bytes at all, so not even a tag.
    ///
    /// Distinct from a zero-length body, which is a tag followed by
    /// nothing and is a file.
    Empty,
    /// A tag that is none of this frame's six.
    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("volume read response frame is empty")
            }
            FrameError::UnknownTag(tag) => {
                write!(f, "unknown volume read response frame tag {tag}")
            }
            FrameError::Error(error) => {
                write!(f, "volume read 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,
        }
    }
}