wl-proxy 0.1.2

Wayland connection proxy
Documentation
use {
    crate::object::{ObjectError, ObjectErrorKind},
    error_reporter::Report,
};

pub(crate) mod prelude {
    #[cfg(feature = "logging")]
    pub(crate) use super::logging::*;
    pub(crate) use {
        super::{NonNullString, NullableString, log_forward, log_send, parse_array, parse_string},
        crate::{
            client::Client,
            endpoint::Endpoint,
            fixed::Fixed,
            handler::{HandlerAccessError, HandlerHolder, HandlerMut, HandlerRef},
            object::{
                ConcreteObject, Object, ObjectCore, ObjectCoreApi, ObjectError, ObjectErrorKind,
                ObjectPrivate, StringError,
            },
            protocols::ObjectInterface,
            state::State,
        },
        std::{
            any::Any,
            cell::RefCell,
            collections::{HashSet, VecDeque},
            fmt::{Debug, Formatter},
            ops::{
                BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Sub, SubAssign,
            },
            os::fd::OwnedFd,
            rc::Rc,
        },
    };
}

#[cfg(feature = "logging")]
mod logging {
    pub use std::os::fd::AsRawFd;
    use {debug_fn::debug_fn, std::fmt::Display, uapi::c};

    pub(crate) fn debug_array(array: &[u8]) -> impl Display + use<'_> {
        debug_fn(move |fmt| {
            fmt.write_str("0x")?;
            if array.is_empty() {
                return fmt.write_str("0");
            }
            for b in array {
                write!(fmt, "{:02x}", b)?;
            }
            Ok(())
        })
    }

    #[inline]
    pub(crate) fn time_since_epoch() -> (u32, u16) {
        let mut ts = c::timespec {
            tv_sec: 0,
            tv_nsec: 0,
        };
        let _ = uapi::clock_gettime(c::CLOCK_REALTIME, &mut ts);
        let sec = ts.tv_sec as u64;
        let nsec = ts.tv_nsec as u64;
        let time = sec.wrapping_mul(1_000_000).wrapping_add(nsec / 1_000) as u32;
        let millis = time / 1_000;
        let micros = (time % 1_000) as u16;
        (millis, micros)
    }
}

#[cold]
pub(crate) fn log_forward(name: &str, e: &ObjectError) {
    log::warn!("Could not forward a {name} message: {}", Report::new(e));
}

#[cold]
pub(crate) fn log_send(name: &str, e: &ObjectError) {
    log::warn!("Could not send a {name} message: {}", Report::new(e));
}

pub(crate) trait StringType {
    type Type<'a>;
    fn null_string<'a>(
        offset: usize,
        name: &'static str,
    ) -> Result<(Self::Type<'a>, usize), ObjectError>;
    fn wrap(s: &str) -> Self::Type<'_>;
}

pub(crate) struct NonNullString;

impl StringType for NonNullString {
    type Type<'a> = &'a str;

    #[inline(always)]
    fn null_string<'a>(
        _offset: usize,
        name: &'static str,
    ) -> Result<(Self::Type<'a>, usize), ObjectError> {
        Err(ObjectError(ObjectErrorKind::NullString(name)))
    }

    #[inline(always)]
    fn wrap(s: &str) -> Self::Type<'_> {
        s
    }
}

pub(crate) struct NullableString;

impl StringType for NullableString {
    type Type<'a> = Option<&'a str>;

    #[inline(always)]
    fn null_string<'a>(
        offset: usize,
        _name: &'static str,
    ) -> Result<(Self::Type<'a>, usize), ObjectError> {
        Ok((None, offset))
    }

    #[inline(always)]
    fn wrap(s: &str) -> Self::Type<'_> {
        Some(s)
    }
}

#[inline]
pub(crate) fn parse_string<'a, T>(
    msg: &'a [u32],
    mut offset: usize,
    name: &'static str,
) -> Result<(T::Type<'a>, usize), ObjectError>
where
    T: StringType,
{
    let Some(&len) = msg.get(offset) else {
        return Err(ObjectError(ObjectErrorKind::MissingArgument(name)));
    };
    offset += 1;
    let len = len as usize;
    let words = ((len as u64 + 3) / 4) as usize;
    if offset + words > msg.len() {
        return Err(ObjectError(ObjectErrorKind::MissingArgument(name)));
    }
    let start = offset;
    offset += words;
    let bytes = &uapi::as_bytes(&msg[start..])[..len];
    if bytes.is_empty() {
        T::null_string(offset, name)
    } else {
        let Ok(s) = str::from_utf8(&bytes[..len - 1]) else {
            return Err(ObjectError(ObjectErrorKind::NonUtf8(name)));
        };
        Ok((T::wrap(s), offset))
    }
}

#[inline]
pub(crate) fn parse_array<'a>(
    msg: &'a [u32],
    mut offset: usize,
    name: &'static str,
) -> Result<(&'a [u8], usize), ObjectError> {
    let Some(&len) = msg.get(offset) else {
        return Err(ObjectError(ObjectErrorKind::MissingArgument(name)));
    };
    offset += 1;
    let len = len as usize;
    let words = ((len as u64 + 3) / 4) as usize;
    if offset + words > msg.len() {
        return Err(ObjectError(ObjectErrorKind::MissingArgument(name)));
    }
    let start = offset;
    offset += words;
    let array = &uapi::as_bytes(&msg[start..])[..len];
    Ok((array, offset))
}