Request
The payload is the tag byte 6 followed by the name of the volume as a varint length and UTF-8 bytes and the path of the file as a varint count of components each a varint length and UTF-8 bytes, in the postcard wire format; the content is not in the request.
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:
[6][name_len: varint][name: utf8 …][path_count: varint][component …]
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.path. The components of the path, as a varint count followed by that many strings, each a varint byte length followed by that many bytes of UTF-8. The count is at least1; no component is empty,.or.., or contains/or NUL.- No content. The request carries no content and no length: the content arrives on the channel the server opens.
- Bytes after the request. A server ignores every byte that follows the last component. A request whose payload extends past the request is not malformed.
- Malformed. A payload with no byte, a payload whose first byte
is not
6, 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/write/client/request/frame.rs:
//! What a client's request frame carries for a volume write.
use serde::{Deserialize, Serialize};
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// Write one file into a volume.
///
/// A name from a listing and a destination inside the volume. Carries
/// no content: the provider asks for the bytes on a channel of its
/// own once it holds the volume — see
/// [`write`](crate::provider::endpoints::volumes::write) for why. No offset and
/// no length: a write replaces whatever is at the path, whole.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
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,
/// The destination, as path components from the volume's root —
/// the same meaning of "path" a mount's `volume_relative_path`
/// has, and the same frame of reference a
/// [`filetree`](crate::provider::endpoints::volumes::filetree) of the
/// volume uses. At least one component, and every component a
/// name: not empty, not `.` or `..`, holding no `/` and no NUL.
/// A parent that does not exist is made; one that exists and is
/// not a directory is the provider's error.
pub path: Vec<String>,
}
/// 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 = 6;
/// 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 write 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 write request frame is empty")
}
FrameError::UnexpectedTag(tag) => {
write!(
f,
"expected volume write request tag {TAG}, found {tag}"
)
}
FrameError::Body(error) => {
write!(f, "volume write 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,
}
}
}