Request
The payload is the tag byte 12 followed by the name of the volume as a varint length and UTF-8 bytes and the change as a varint discriminant, 0 for a size, 1 for a mode, 2 for both, followed by the fields of that kind, in the postcard wire format.
The payload of the request is the tag byte followed by the request in the postcard wire format, as the postcard reading of Notation provides:
[12][name_len: varint][name: utf8 …][0][bytes: varint]
[14][name_len: varint][name: utf8 …][1][mode: 0 | 1 | 2]
[14][name_len: varint][name: utf8 …][2][bytes: varint][mode: 0 | 1 | 2]
name. The name of the volume, UTF-8, preceded by its byte length as a varint. It is thenameof a volume in the client’s volumes::list listing.change. The change, as a postcard enum: a varint discriminant, which is0,1or2, followed by the fields of that kind.0is a size and is followed bybytes.1is a mode and is followed bymode.2is both and is followed bybytesand bymode, in that order. A discriminant other than0,1and2does not decode.bytes. The size the volume has after the edit, in bytes, as a varint. It is absolute, not a difference from the current size.mode. The mode the volume has after the edit, as one byte, the postcard discriminant of the enum:0ispersistent,1isephemeral,2isread_only. A byte other than0,1and2does not decode.- Bytes after the request. A server ignores every byte that follows the last field of the change. A request whose payload extends past the request is not malformed.
- Malformed. A payload with no byte, a payload whose first byte
is not
12, and a payload whose bytes after the tag do not decode as the request are malformed. Endpoints states how a server answers a request it cannot read.
The request is defined by diverge-sdk-rs/src/provider/endpoints/volumes/edit/client/request/frame.rs:
//! What a client's request frame carries for a volume edit.
use serde::{Deserialize, Serialize};
use super::Change;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// Change a volume's size, its mode, or both.
///
/// The two things about a volume that can be edited, and an edit is
/// one [`Change`]: the size, the mode, or both at once. Its
/// [`name`](Self::name) cannot be edited — the name is the handle,
/// and a handle that changed would leave every
/// [`VolumeMount`](crate::shared::containers::request::VolumeMount)
/// naming something that is no longer there.
///
/// So this names the volume and states the change. Two fields, and
/// only one of them is being set.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Frame {
/// Which volume, by the name a listing gave it.
///
/// Names come from
/// [`Volume::name`](crate::provider::endpoints::volumes::list::server::response::Volume::name)
/// and mean nothing outside the provider that published them.
pub name: String,
/// What to change: the size, the mode, or both. See [`Change`].
pub change: Change,
}
/// This frame's tag among the scope-opening requests.
///
/// One byte at the front of the payload, which is what tells a reader
/// which request it holds. The frame layer does not discriminate them
/// — [`ClientFrame::Request`](crate::wire::frame::client::ClientFrame::Request)
/// is one type carrying bytes — so the distinction has to be in the
/// bytes, and each request owns the value that names it.
///
/// See the table in [`endpoints`](crate::provider::endpoints) for the whole
/// allocation. The values are chosen across modules that do not know
/// about each other, so the table is the only place they can be seen
/// at once.
const TAG: u8 = 12;
/// Postcard, matching the rest of [`volumes`](crate::provider::endpoints::volumes).
impl Encode for Frame {
/// Postcard's own failure. The tag cannot fail.
type Error = postcard::Error;
fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
out.extend_from_slice(&[TAG]);
postcard::to_io(self, &mut *out)?;
Ok(())
}
}
impl Decode<'_> for Frame {
/// Three ways to fail, and only one of them is postcard's.
type Error = FrameError;
fn decode(bytes: &[u8]) -> Result<Self, Self::Error> {
let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
if *tag != TAG {
return Err(FrameError::UnexpectedTag(*tag));
}
postcard::from_bytes(rest).map_err(FrameError::Body)
}
}
/// A volume edit request that could not be read.
#[derive(Debug)]
pub enum FrameError {
/// No bytes at all, so not even a tag.
Empty,
/// A tag naming some other request.
///
/// A reader that dispatched on the tag will not see this. One that
/// assumed which request it held, and was wrong, will — which is
/// the point of checking a tag rather than skipping it.
UnexpectedTag(u8),
/// The request did not parse.
Body(postcard::Error),
}
impl std::fmt::Display for FrameError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FrameError::Empty => {
f.write_str("volume edit request frame is empty")
}
FrameError::UnexpectedTag(tag) => {
write!(
f,
"expected volume edit request tag {TAG}, found {tag}"
)
}
FrameError::Body(error) => {
write!(f, "volume edit request did not parse: {error}")
}
}
}
}
impl std::error::Error for FrameError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FrameError::Body(error) => Some(error),
FrameError::Empty | FrameError::UnexpectedTag(_) => None,
}
}
}