Request
The payload is the tag byte 5 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 payload of the request is the tag byte followed by the request in the postcard wire format, as the postcard reading of Notation provides:
[5][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.- 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
5, 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/read/client/request/frame.rs:
//! What a client's request frame carries for a volume read.
use serde::{Deserialize, Serialize};
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// Read one file out of a volume.
///
/// A name from a listing and a path inside the volume. No offset and
/// no length: a read starts at the beginning and runs to the end,
/// which a volume at rest has.
#[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 file, 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 provider descends names and never resolves a path, so there
/// is no component that means "up".
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 = 5;
/// 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 read 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 read request frame is empty")
}
FrameError::UnexpectedTag(tag) => {
write!(
f,
"expected volume read request tag {TAG}, found {tag}"
)
}
FrameError::Body(error) => {
write!(f, "volume read 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,
}
}
}