# /filesystem/write

> One file into the container: the server sends the name of the file, its content in one or more pieces, and one empty message; the proxy sends exactly one answer, ok or an error, and closes the connection.

Canonical: https://provider.diverge.network/2.3.0/proxy/filesystem/write/
Specification revision: 2.3.0

The server opens `/filesystem/write` and sends, in order, the request
defined below as JSON, naming the destination; the content, one piece
per message; and exactly one empty message, which ends the content.
The proxy sends exactly one message and closes the connection.

```text
server → proxy:   [request JSON] [bytes …] … [bytes …] []
proxy → server:   [kind: u8][message …]                   once
```

- **The end of the content.** A piece is never empty. The first empty
  message ends the content. The file is the pieces concatenated, in
  order.
- **The answer.** A first byte of `0` states that the file was
  written. A first byte of `1` is followed by a message stating why
  the file was not written; a destination whose parent directory does
  not exist is such a case.
- **An abandoned write.** A close by the server before the empty
  message abandons the write, and the destination remains as it was.
  An abrupt end by either party is a write that failed, and the
  destination remains as it was.
- **No repetition.** No party retries a write.

The request is defined by
`diverge-provider-sdk/src/container_proxy/filesystem/write/request/request.rs`:

```rust
//! The destination.

use serde::{Deserialize, Serialize};

use crate::decode::Decode;
use crate::encode::{Encode, Writer};

/// The file to write, the first message on `/filesystem/write`.
///
/// No length and no mode: the content follows as
/// [`Frame`](super::Frame)s until the empty one, and what the file
/// looks like on disk is the container's business.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct Request {
    /// The destination, as path components from the container's
    /// root — the same meaning of "path" as everywhere else in this
    /// crate, and the same frame of reference a
    /// [`filetree`](crate::shared::filetree) stream uses. Components
    /// rather than a joined string: a path is a sequence, and joining
    /// it would invent a separator that then has to be escaped out
    /// of names containing it.
    pub path: Vec<String>,
}

impl Encode for Request {
    /// The ordinary JSON failure.
    type Error = serde_json::Error;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
        serde_json::to_writer(out, self)
    }
}

impl Decode<'_> for Request {
    /// The ordinary JSON failure.
    type Error = serde_json::Error;

    fn decode(bytes: &[u8]) -> Result<Self, Self::Error> {
        serde_json::from_slice(bytes)
    }
}
```

A piece is defined by
`diverge-provider-sdk/src/container_proxy/filesystem/write/request/frame.rs`:

```rust
//! A chunk of the content.

use std::convert::Infallible;

use crate::encode::{Encode, Writer};

/// One message of content on `/filesystem/write`, every message after the
/// first: bytes, verbatim, in order. An EMPTY one is not content —
/// it is the end of it, after which the container answers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Frame<'a>(
    /// The bytes, borrowed from the message they arrived in.
    pub &'a [u8],
);

impl Encode for Frame<'_> {
    /// [`Infallible`]: bytes copied.
    type Error = Infallible;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Infallible> {
        out.extend_from_slice(self.0);
        Ok(())
    }
}

impl<'a> Frame<'a> {
    /// Decode one message: the bytes, kept.
    pub fn decode(bytes: &'a [u8]) -> Result<Self, Infallible> {
        Ok(Frame(bytes))
    }
}
```

The answer is defined by
`diverge-provider-sdk/src/container_proxy/filesystem/write/response/frame.rs`:

```rust
//! Whether the file landed.

use std::convert::Infallible;

use super::FrameError;
use crate::encode::{Encode, Writer};

/// The one message the container sends on `/filesystem/write`, after the empty
/// chunk and before the close.
///
/// ```text
/// [kind: u8][message…]
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Frame<'a> {
    /// Kind `0`. The file is written, whole.
    Ok,
    /// Kind `1`. It is not, and this says why, for a reader rather
    /// than a program: a missing parent, a path that is a directory,
    /// a disk that is full. Nothing here is enumerated, because what
    /// a container refuses is its filesystem's business.
    Error(&'a str),
}

impl Encode for Frame<'_> {
    /// [`Infallible`]: a kind byte and bytes copied.
    type Error = Infallible;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Infallible> {
        match self {
            Frame::Ok => out.extend_from_slice(&[0]),
            Frame::Error(message) => {
                out.extend_from_slice(&[1]);
                out.extend_from_slice(message.as_bytes());
            }
        }
        Ok(())
    }
}

impl<'a> Frame<'a> {
    /// Decode the one message. The message borrows from `bytes`.
    pub fn decode(bytes: &'a [u8]) -> Result<Self, FrameError> {
        let (kind, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
        match *kind {
            0 => Ok(Frame::Ok),
            1 => std::str::from_utf8(rest)
                .map(Frame::Error)
                .map_err(|_| FrameError::MessageUtf8),
            other => Err(FrameError::UnknownKind(other)),
        }
    }
}
```

The ways in which the answer fails to decode are defined by
`diverge-provider-sdk/src/container_proxy/filesystem/write/response/error.rs`:

```rust
//! Why the answer on `/filesystem/write` could not be decoded.

use std::error;
use std::fmt;

/// An answer that could not be read.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FrameError {
    /// No bytes at all, so not even a kind.
    Empty,
    /// A kind this answer does not define.
    UnknownKind(u8),
    /// An error message that is not UTF-8.
    MessageUtf8,
}

impl fmt::Display for FrameError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FrameError::Empty => f.write_str("write answer is empty"),
            FrameError::UnknownKind(kind) => {
                write!(f, "unknown write answer kind {kind}")
            }
            FrameError::MessageUtf8 => {
                f.write_str("write error message is not utf-8")
            }
        }
    }
}

impl error::Error for FrameError {}
```
