maelstrom-client-process 0.14.0

Client library background process for Maelstrom.
use crate::artifact_pusher::{construct_upload_name, start_task_inner, Receiver, SuccessCb};
use crate::progress::{ProgressTracker, UploadProgressReader};
use anyhow::{anyhow, Context as _, Result};
use maelstrom_base::{
    proto::{ArtifactPusherToBroker, BrokerToArtifactPusher, Hello},
    ArtifactUploadLocation, Sha256Digest,
};
use maelstrom_util::{async_fs::Fs, config::common::BrokerAddr, net, net::AsRawFdExt as _};
use slog::Logger;
use std::path::{Path, PathBuf};
use tokio::{
    io::{self, AsyncReadExt as _},
    net::TcpStream,
    task::JoinSet,
};

pub async fn push_one_artifact(
    stream: Option<TcpStream>,
    upload_tracker: ProgressTracker,
    broker_addr: BrokerAddr,
    path: PathBuf,
    digest: Sha256Digest,
    success_callback: SuccessCb,
    log: Logger,
) -> Result<(TcpStream, ())> {
    push_one_artifact_inner(
        stream,
        upload_tracker,
        broker_addr,
        &path,
        digest,
        success_callback,
        log,
    )
    .await
    .with_context(|| format!("pushing artifact {}", path.display()))
}

async fn push_one_artifact_inner(
    stream: Option<TcpStream>,
    upload_tracker: ProgressTracker,
    broker_addr: BrokerAddr,
    path: &Path,
    digest: Sha256Digest,
    success_callback: SuccessCb,
    log: Logger,
) -> Result<(TcpStream, ())> {
    let mut stream = match stream {
        Some(stream) => stream,
        None => {
            let mut stream = TcpStream::connect(broker_addr.inner())
                .await?
                .set_socket_options()?;
            net::write_message_to_async_socket(&mut stream, &Hello::ArtifactPusher, &log).await?;
            stream
        }
    };

    let fs = Fs::new();
    let file = fs.open_file(&path).await?;
    let size = file.metadata().await?.len();

    let upload_name = construct_upload_name(&digest, path);
    let prog = upload_tracker.new_task(&upload_name, size);

    let mut file = UploadProgressReader::new(prog, file.chain(io::repeat(0)).take(size));

    net::write_message_to_async_socket(&mut stream, &ArtifactPusherToBroker(digest, size), &log)
        .await?;
    let copied = io::copy(&mut file, &mut stream).await?;
    assert_eq!(copied, size);

    let BrokerToArtifactPusher(resp) =
        net::read_message_from_async_socket(&mut stream, &log).await?;

    resp.map_err(|e| anyhow!("Error from broker: {e}"))?;

    success_callback(ArtifactUploadLocation::TcpUpload);

    Ok((stream, ()))
}

pub fn start_task(
    join_set: &mut JoinSet<Result<()>>,
    receiver: Receiver,
    broker_addr: BrokerAddr,
    upload_tracker: ProgressTracker,
    log: Logger,
) {
    start_task_inner(
        join_set,
        receiver,
        move |stream, path, digest, success_callback| {
            push_one_artifact(
                stream,
                upload_tracker.clone(),
                broker_addr,
                path,
                digest,
                success_callback,
                log.clone(),
            )
        },
    )
}