# Request

> The payload is the tag byte 2 followed by the mount as JSON: the path, as components from the root of the container, and the kind, file or directory.

Canonical: https://protocol.diverge.network/2.3.0/container-proxy-endpoints/fuse-mount/request/
Specification revision: 2.3.0

```text
[2][request JSON …]
```

- **`path`.** An array of strings: the components of the mount point,
  from the root of the container. Never empty; no component empty,
  `.` or `..`.
- **`kind`.** The string `file` or the string `directory`.
- **No id.** The request carries no id. The scope is the mount, and
  the client, which relays each ask to its client with the id the
  client gave the mount, knows the scope by that id.
- **Malformed.** A payload that does not decode as stated is answered
  by a response finish that no response precedes.

The request is defined by `diverge-sdk-rs/src/container_proxy/outside/endpoints/fuse/mount/client/request/frame.rs`:

```rust
//! What a client's request frame carries for a FUSE mount.

use serde::{Deserialize, Serialize};

use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
use crate::shared::containers::fuse::Kind;

/// Make one FUSE mount, as the container request named it.
///
/// One [`FuseMount`](crate::shared::containers::request::FuseMount) of
/// the request, with which list it was on made explicit as the
/// [`kind`](Self::kind). The path is components from the container's
/// root, never empty, no component empty or `.` or `..`.
///
/// # The id stays with the server
///
/// A [`FuseMount`](crate::shared::containers::request::FuseMount)
/// carries the caller's id for the mount, and this does not: the scope
/// this opens IS the mount, every ask the mount makes rides it, and
/// the server — which relays each ask to the caller as the provider
/// protocol's own — knows the scope by the id and puts it back. A
/// container cannot name a mount the caller did not make, because it
/// never holds an id at all.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Frame {
    /// Where the mount goes, as components from the container's
    /// root.
    pub path: Vec<String>,
    /// One regular file, or a directory tree.
    pub kind: Kind,
}

/// 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
/// [`container_proxy_endpoints`](crate::container_proxy::outside) 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 = 2;

impl Encode for Frame {
    /// The ordinary JSON failure. The tag cannot fail.
    type Error = serde_json::Error;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
        out.extend_from_slice(&[TAG]);
        serde_json::to_writer(out, self)
    }
}

impl Decode<'_> for Frame {
    /// Three ways to fail, and only one of them is JSON.
    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));
        }
        serde_json::from_slice(rest).map_err(FrameError::Body)
    }
}

/// A fuse mount 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(serde_json::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("fuse mount request frame is empty")
            }
            FrameError::UnexpectedTag(tag) => {
                write!(f, "expected fuse mount request tag {TAG}, found {tag}")
            }
            FrameError::Body(error) => {
                write!(f, "fuse mount 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,
        }
    }
}
```

and by `diverge-sdk-rs/src/shared/containers/fuse/entry.rs`:

```rust
//! One entry of a listed directory.

use serde::{Deserialize, Serialize};

use super::{ResponseEncodeError, ResponseError, prefixed};
use crate::wire::encode::Writer;

/// What an entry is — and, on a
/// [`mount`](crate::container_proxy::outside::endpoints::fuse::mount) request, which kind
/// of mount: one file, or a tree. One byte on the binary answers;
/// `"file"` or `"directory"` where it rides JSON.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Kind {
    /// A regular file. Kind `0`.
    File,
    /// A directory. Kind `1`.
    Directory,
}

/// Kind byte for a file.
const FILE: u8 = 0;

/// Kind byte for a directory.
const DIRECTORY: u8 = 1;

impl Kind {
    /// The one byte that says which.
    pub(crate) fn byte(self) -> u8 {
        match self {
            Kind::File => FILE,
            Kind::Directory => DIRECTORY,
        }
    }

    /// Which, from its byte.
    pub(crate) fn from_byte(byte: u8) -> Result<Self, ResponseError> {
        match byte {
            FILE => Ok(Kind::File),
            DIRECTORY => Ok(Kind::Directory),
            other => Err(ResponseError::UnknownKind(other)),
        }
    }
}

/// One entry of a directory, as a [`list`](super::list) answers it.
///
/// ```text
/// [kind: u8][name_len: u16 BE][name: utf8…]
/// ```
///
/// The name is the entry's own, one path component, never `.` or
/// `..`. What a listing says is what a `readdir` needs and no more;
/// an entry's size is a [`stat`](super::stat) of its own.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Entry<'a> {
    /// The entry's name.
    pub name: &'a str,
    /// File or directory.
    pub kind: Kind,
}

impl Entry<'_> {
    /// Write one entry.
    pub(crate) fn encode(&self, out: &mut Writer<'_>) -> Result<(), ResponseEncodeError> {
        out.extend_from_slice(&[self.kind.byte()]);
        prefixed::put(out, self.name.as_bytes()).map_err(ResponseEncodeError::NameLength)
    }
}

impl<'a> Entry<'a> {
    /// Split one entry off the front: the entry, then the rest.
    pub(crate) fn decode(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), ResponseError> {
        let (kind, rest) = bytes.split_first().ok_or(ResponseError::Truncated)?;
        let kind = Kind::from_byte(*kind)?;
        let (name, rest) = prefixed::take(rest).map_err(|_| ResponseError::Truncated)?;
        Ok((
            Entry {
                name: std::str::from_utf8(name).map_err(|_| ResponseError::NameUtf8)?,
                kind,
            },
            rest,
        ))
    }
}
```
