ipcez 0.1.0

Rust library for ipcez.
Documentation
//! Naming and signaling for named Windows events and POSIX named semaphores used after
//! sending on local transports, so the receiver can wait on the event/semaphore before reading.

/// Returns the named event/semaphore name for the "data ready" signal for the given pipe/endpoint name.
/// Windows: `Global\<pipe_name>.data_ready`. Linux: `/ipcez_<sanitized>_data_ready` (POSIX semaphore names must start with `/` and contain no further slashes).
#[cfg(windows)]
pub(crate) fn data_ready_event_name(pipe_name: &str) -> String {
    format!(r"Global\{}.data_ready", pipe_name)
}

#[cfg(unix)]
pub(crate) fn data_ready_event_name(pipe_name: &str) -> String {
    const MAX_SEM_NAME_LEN: usize = 251;
    let sanitized: String = pipe_name
        .chars()
        .map(|c| match c {
            '/' | '\\' | '.' => '_',
            _ => c,
        })
        .take(MAX_SEM_NAME_LEN.saturating_sub(20))
        .collect();
    let base = if sanitized.is_empty() {
        "ipcez_default".to_string()
    } else {
        format!("ipcez_{}", sanitized)
    };
    format!("/{}_data_ready", base)
}

/// Derives the "data acked" event/semaphore name from the "data ready" name (string replacement).
/// Used when only the data_ready name is available (e.g. from InnerSocket).
pub(crate) fn data_acked_name_from_data_ready(data_ready_name: &str) -> String {
    if data_ready_name.contains(".data_ready") {
        data_ready_name.replace(".data_ready", ".data_acked")
    } else {
        data_ready_name.replace("_data_ready", "_data_acked")
    }
}

/// Signals a named Windows event (best-effort). Used after send on named pipe so the receiver can wait on it.
/// If the event does not exist, this is a no-op; send_message() still succeeds.
#[cfg(windows)]
pub(crate) fn signal_named_event(name: &str) {
    use windows_sys::Win32::Foundation::CloseHandle;
    use windows_sys::Win32::System::Threading::{OpenEventW, SetEvent, EVENT_MODIFY_STATE};
    let wide: Vec<u16> = name.encode_utf16().chain(std::iter::once(0)).collect();
    let handle = unsafe { OpenEventW(EVENT_MODIFY_STATE, 0, wide.as_ptr()) };
    if handle != 0 {
        unsafe {
            SetEvent(handle);
            CloseHandle(handle);
        }
    }
}

/// Signals a named POSIX semaphore (best-effort). Used after send so the receiver can wait on it.
/// If the semaphore does not exist, this is a no-op; send_message() still succeeds.
#[cfg(unix)]
pub(crate) fn signal_named_event(name: &str) {
    if let Ok(mut sem) = named_sem::NamedSemaphore::create(name, 0) {
        let _ = sem.post();
    }
}