> ## Documentation Index
> Fetch the complete documentation index at: https://hayate.shiina.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# HayateSender: Send Files and Directories from Rust

> HayateSender is a builder for sending files or directories over QUIC. Configure target, code, compression, and hash algorithm, then call .send().

`HayateSender` is the primary high-level interface for sending files and directories over an encrypted QUIC connection. It follows a builder pattern: configure the sender with chained method calls, then call `.send()` to establish the connection, perform the cryptographic handshake, and stream the payload. The builder handles both Direct Mode (connect to a known IP address) and Pairing Mode (discover the receiver on the LAN using a shared passphrase) with the same API surface.

## Builder Methods

<ParamField path="HayateSender::new()" type="HayateSender">
  Creates a new `HayateSender` with default configuration. Defaults: compression enabled, hash algorithm `"blake3"`. Neither a target address nor a pairing code is set — you must call `.target()` or `.code()` before calling `.send()`.
</ParamField>

<ParamField path=".target(addr: SocketAddr)" type="HayateSender">
  Sets the receiver's socket address for **Direct Mode**. The sender will initiate a QUIC connection directly to this address. Calling `.target()` clears any previously set `.code()` — the two modes are mutually exclusive.
</ParamField>

<ParamField path=".code(phrase: String)" type="HayateSender">
  Sets the shared passphrase for **Pairing Mode**. The sender will broadcast its availability over the local subnet (via mDNS and UDP broadcast) so the receiver can locate and connect to it automatically. The passphrase is also mixed into the HKDF key derivation, so peers that do not know it cannot authenticate the session. Calling `.code()` clears any previously set `.target()`.
</ParamField>

<ParamField path=".compress(enabled: bool)" type="HayateSender">
  Enables or disables Zstd compression for the transfer payload. Default: `true`. Compression reduces network bytes at the cost of CPU time; disable it when sending already-compressed formats (e.g., JPEG, MP4, ZIP).
</ParamField>

<ParamField path=".hash_algo(algo: String)" type="HayateSender">
  Sets the integrity algorithm used to checksum the transferred payload. Accepted values: `"blake3"` (default) or `"sha256"`. The chosen algorithm is transmitted in the encrypted metadata frame so the receiver can verify the same digest independently.
</ParamField>

## Sending

```rust theme={null}
pub async fn send(
    self,
    path: impl AsRef<Path>,
    progress_cb: impl FnMut(u64) -> Result<(), EngineError> + Send + 'static,
) -> Result<String, EngineError>
```

Connects to the receiver, performs the X25519 + HKDF handshake, and streams the payload. Returns the hex-encoded checksum of the transferred data on success.

* **`path`** — accepts anything that implements `AsRef<Path>`. Works for both individual files and entire directories; Hayate detects the type automatically and wraps directories in a streaming tar archive.
* **`progress_cb`** — called periodically with the total number of bytes written to the network so far. Return `Ok(())` to continue the transfer. Return `Err(EngineError::Cancelled(...))` to abort immediately.

## Low-Level Methods

These methods are available for advanced callers who manage their own QUIC connection and want fine-grained control over each transfer stage. They were made public in **v6.0.0**.

```rust theme={null}
pub fn build_metadata(
    &self,
    path: &Path,
) -> Result<(Metadata, u64), EngineError>
```

Builds the `Metadata` struct and estimates the total byte size for `path` without opening any network connection. Useful when you want to display transfer details to the user before committing to a connection.

```rust theme={null}
pub async fn send_file(
    &self,
    path: &Path,
    key: &[u8; 32],
    cipher_id: u8,
    hash_algo: &str,
    stream: &mut compio_quic::SendStream,
    progress_cb: impl FnMut(u64) -> Result<(), EngineError> + Send + 'static,
) -> Result<String, EngineError>
```

Streams a single file over an already-established and handshook QUIC send stream. Returns the hex checksum.

```rust theme={null}
pub async fn send_directory(
    &self,
    dir: &Path,
    key: &[u8; 32],
    cipher_id: u8,
    hash_algo: &str,
    stream: &mut compio_quic::SendStream,
    progress_cb: impl FnMut(u64) -> Result<(), EngineError> + Send + 'static,
) -> Result<String, EngineError>
```

Packages a directory as a streaming tar archive and sends it over an already-established QUIC send stream. Path-traversal protection is enforced during extraction on the receiver side.

## Complete Examples

<CodeGroup>
  ```rust Pairing Mode theme={null}
  use hayate::HayateSender;

  #[compio::main]
  async fn main() -> Result<(), hayate::EngineError> {
      let sender = HayateSender::new()
          .code("apple-bravo-charlie".to_owned());

      let checksum = sender.send("./report.pdf", |bytes| {
          println!("Progress: {bytes} bytes sent");
          Ok(())
      }).await?;

      println!("Done. Checksum: {checksum}");
      Ok(())
  }
  ```

  ```rust Direct Mode theme={null}
  use std::net::SocketAddr;
  use hayate::HayateSender;

  #[compio::main]
  async fn main() -> Result<(), hayate::EngineError> {
      let target: SocketAddr = "192.168.1.50:50001".parse().unwrap();

      let sender = HayateSender::new()
          .target(target)
          .compress(true)
          .hash_algo("sha256".to_owned());

      let checksum = sender.send("./photos", |bytes| {
          println!("{bytes} bytes sent");
          Ok(())
      }).await?;

      println!("Checksum: {checksum}");
      Ok(())
  }
  ```
</CodeGroup>

<Note>
  `.target()` and `.code()` are mutually exclusive — the last one you call wins and clears the other. If you call `.target()` followed by `.code()`, the sender will use Pairing Mode. If you call `.code()` followed by `.target()`, it will use Direct Mode.
</Note>
