Response

The server sends exactly one response, whose payload is exactly the byte 0 when the volume exists, exactly the byte 1 when the server has insufficient capacity for it, exactly the byte 2 when the server keeps no volume of the mode stated, exactly the byte 3 when the server keeps no volume, or the byte 4 followed by an error as one JSON value, and the response finish follows it.

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

[0]                  created
[1]                  not created: insufficient capacity
[2]                  not created: the server keeps no volume of the mode stated
[3]                  not created: the server keeps no volume
[4][error JSON …]    not created
  • 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 provides; whether the volume exists is then not stated, and the client learns it from a volumes::list scope.
  • Created. A payload whose first byte is 0 states that the volume exists. The server sends it after the volume exists; a volumes::list scope the client opens after receiving it lists the volume, and a list scope the client holds open is sent the volume as added. The server sends nothing after the byte 0, and a client ignores every byte that follows it.
  • Insufficient capacity. A payload whose first byte is 1 states that the server cannot reserve the size stated and did not create the volume. No volume exists by that name as a result of the request. The server sends nothing after the byte 1, and a client ignores every byte that follows it.
  • Mode unsupported. A payload that is the byte 2 and nothing else states that the server holds out no mode object for the mode the request states, and did not create the volume. No volume exists by that name as a result of the request. The server sends it before it reserves anything, and a client ignores every byte that follows it.
  • Unsupported. A payload that is the byte 3 and nothing else states that the server holds out none of the three mode objects and keeps no volume, as Capabilities provides. It is the only response, sent before anything is looked up; a client ignores every byte that follows it.
  • The error. A payload whose first byte is 4 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 states that the volume does not exist as a result of the request, and that the reason is neither capacity nor the mode.
  • Malformed. A payload with no byte, a payload whose first byte is none of 0, 1, 2, 3 and 4, and a payload whose bytes after 4 are not one JSON value are malformed.

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

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

use std::fmt;

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

/// The volume exists, or there was no room for it, or it does not
/// exist for some other reason.
///
/// One of these on channel `0`, then the scope finishes. A payload
/// leads with one byte saying which — `0` for
/// [`Created`](Self::Created), `1` for
/// [`InsufficientCapacity`](Self::InsufficientCapacity), `2` for
/// [`ModeUnsupported`](Self::ModeUnsupported), `3` for
/// [`Unsupported`](Self::Unsupported), `4` for [`Error`](Self::Error)
/// — and for the first four there is nothing after it, because saying
/// so IS the whole message.
///
/// | the scope ends with | means |
/// |----------------------|-------|
/// | [`Created`](Self::Created), then a finish | the volume exists and a listing will show it |
/// | [`InsufficientCapacity`](Self::InsufficientCapacity), then a finish | the provider cannot reserve that many bytes, and nothing exists |
/// | [`ModeUnsupported`](Self::ModeUnsupported), then a finish | the provider has no volumes of the mode asked for, and nothing exists |
/// | an [`Error`](Self::Error), then a finish | it does not exist, for some other reason, and nothing partial does |
///
/// # Insufficient capacity is an answer, not an error
///
/// A provider that cannot reserve the size asked for says so with
/// [`InsufficientCapacity`](Self::InsufficientCapacity) rather than
/// with an [`Error`](Self::Error) a caller could not tell from any
/// other failure. The distinction is what a caller acts on: a size
/// the provider has no room for is one to ask smaller, and a failure
/// is not. A mode the provider's
/// [`info`](crate::provider::endpoints::info) holds out no capability
/// for is [`ModeUnsupported`](Self::ModeUnsupported), likewise: a
/// mode to ask otherwise, or a provider to ask elsewhere.
///
/// # Why it does not answer with the volume
///
/// Because the caller already knows both fields. It chose the
/// [`name`](crate::provider::endpoints::volumes::create::client::request::Frame::name),
/// and a
/// [`created`](crate::provider::endpoints::volumes::list::server::response::Volume::created)
/// it can predict to the second is not news. Sending a
/// [`Volume`](crate::provider::endpoints::volumes::list::server::response::Volume)
/// back would be echoing a request with a timestamp stapled to it, and
/// a caller that wants the canonical record asks for a
/// [`list`](crate::provider::endpoints::volumes::list) — which is the same
/// answer every other caller gets, rather than a second version of the
/// truth minted here.
///
/// # The scope ends, and the volume does not
///
/// Unlike a [`container`](crate::provider::endpoints::containers), whose
/// scope IS its life. A volume outlives the request
/// that made it and every connection the caller ever holds; it goes
/// away when a
/// [`delete`](crate::provider::endpoints::volumes::delete) says so and not
/// before.
///
/// Which is what makes it worth having. A caller mounts one into a
/// laboratory, the laboratory stops, and the work is still there.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
    /// The volume exists. Tag `0`.
    Created,
    /// The provider cannot reserve that many bytes, and nothing
    /// exists. Tag `1`.
    InsufficientCapacity,
    /// The provider has no volumes of the mode asked for, and nothing
    /// exists. Tag `2`.
    ModeUnsupported,
    /// The provider keeps no volumes: no mode is among its capabilities,
    /// see [`info`](crate::provider::endpoints::info), and every volume
    /// endpoint answers this. Tag `3`.
    ///
    /// Carries nothing. Nothing exists; a caller asks another
    /// provider.
    Unsupported,
    /// A failure. Tag `4`.
    ///
    /// See [`shared::error::Error`](crate::shared::error::Error) for
    /// why it says so little.
    Error(Error),
}

/// Tag for [`Frame::Created`].
const CREATED: u8 = 0;

/// Tag for [`Frame::InsufficientCapacity`].
const INSUFFICIENT_CAPACITY: u8 = 1;

/// Tag for [`Frame::ModeUnsupported`].
const MODE_UNSUPPORTED: u8 = 2;

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

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

/// A tag, and for a failure the JSON after it. Postcard encodes the
/// rest of [`volumes`](crate::provider::endpoints::volumes) and encodes nothing
/// here — an [`Error`](Frame::Error) is a
/// [`serde_json::Value`], which deserializes through
/// `deserialize_any` and so cannot come back out of a format with no
/// self-description. The tag chooses the format, one variant at a
/// time.
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::Created => {
                out.extend_from_slice(&[CREATED]);
                Ok(())
            }
            Frame::InsufficientCapacity => {
                out.extend_from_slice(&[INSUFFICIENT_CAPACITY]);
                Ok(())
            }
            Frame::ModeUnsupported => {
                out.extend_from_slice(&[MODE_UNSUPPORTED]);
                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 {
            CREATED => Ok(Frame::Created),
            INSUFFICIENT_CAPACITY => Ok(Frame::InsufficientCapacity),
            MODE_UNSUPPORTED => Ok(Frame::ModeUnsupported),
            UNSUPPORTED => Ok(Frame::Unsupported),
            ERROR => {
                Error::decode(rest).map(Frame::Error).map_err(FrameError::Error)
            }
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}

/// A volume creation 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 five.
    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 creation result frame is empty")
            }
            FrameError::UnknownTag(tag) => {
                write!(f, "unknown volume creation result frame tag {tag}")
            }
            FrameError::Error(error) => {
                write!(f, "volume creation 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,
        }
    }
}