samply 0.13.1

A command line profiler for macOS and Linux.
use std::error::Error;
use std::path::Path;

use super::file_channel::BidiChannelCreator;
use super::shared::{
    ChildToParentMsgWrapper, InitMessage, ParentToChildMsgWrapper, PKG_NAME, PKG_VERSION,
};
use super::traits::{UtilityProcess, UtilityProcessChild};

pub fn run_child<T: UtilityProcess>(ipc_directory: &Path, child: T::Child) {
    match run_child_internal::<T>(ipc_directory, child) {
        Ok(()) => {}
        Err(e) => log::error!("Error running elevated helper: {e:?}"),
    }
}

pub fn run_child_internal<T: UtilityProcess>(
    ipc_directory: &Path,
    mut child: T::Child,
) -> Result<(), Box<dyn Error + Send + Sync>> {
    let (mut receiver, mut sender) = BidiChannelCreator::open_in_child(ipc_directory)?;

    let init_msg: ParentToChildMsgWrapper<T::ParentToChildMsg> = receiver.recv_blocking()?;
    if let Err(e) = check_init_msg::<T>(init_msg) {
        let _ = sender.send(ChildToParentMsgWrapper::Err(e.to_string()));
        return Err(e);
    }
    sender.send(ChildToParentMsgWrapper::AckInit)?;

    let mut shutting_down = false;
    while !shutting_down {
        let msg: ParentToChildMsgWrapper<T::ParentToChildMsg> = receiver.recv_blocking()?;
        let reply: ChildToParentMsgWrapper<T::ChildToParentMsg> = match msg {
            init_msg @ ParentToChildMsgWrapper::Init { .. } => ChildToParentMsgWrapper::Err(
                format!("Unexpected init message after initialization: {init_msg:?}"),
            ),
            ParentToChildMsgWrapper::Msg(msg) => match child.handle_message(msg) {
                Ok(reply) => ChildToParentMsgWrapper::AckMsg(reply),
                Err(e) => ChildToParentMsgWrapper::Err(e.to_string()),
            },
            ParentToChildMsgWrapper::Shutdown => {
                shutting_down = true;
                ChildToParentMsgWrapper::AckShutdown
            }
        };
        sender.send(reply)?;
    }

    Ok(())
}

fn check_init_msg<T: UtilityProcess>(
    init_msg: ParentToChildMsgWrapper<T::ParentToChildMsg>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
    let ParentToChildMsgWrapper::Init(init_msg) = init_msg else {
        return Err(format!("Unexpected init message type: {init_msg:?}").into());
    };

    let InitMessage {
        helper_type,
        pkg_name,
        pkg_version,
    } = init_msg;

    if helper_type != T::PROCESS_TYPE {
        return Err(format!(
            "Unexpected helper_type {helper_type}, expected {}",
            T::PROCESS_TYPE
        )
        .into());
    }
    if pkg_name != PKG_NAME {
        return Err(format!("Unexpected pkg_name {pkg_name}, expected {PKG_NAME}").into());
    }
    if pkg_version != PKG_VERSION {
        return Err(format!("Unexpected pkg_version {pkg_version}, expected {PKG_VERSION}").into());
    }
    Ok(())
}