tokio-dbus-core 0.2.0

Pure Rust D-Bus implementation for Tokio.
Documentation
use crate::proto::Type;

use super::stack::{Stack, StackValue};
use super::{MAX_CONTAINER_DEPTH, MAX_DEPTH, SignatureError, SignatureErrorKind};

#[derive(Default, Debug, Clone, Copy)]
#[repr(u8)]
pub(super) enum Kind {
    #[default]
    None,
    Array,
    Struct,
    Dict,
}

impl StackValue for (Kind, u8) {
    const DEFAULT: Self = (Kind::None, 0);
}

impl StackValue for Kind {
    const DEFAULT: Self = Kind::None;
}

#[allow(unused_assignments)]
pub(super) const fn validate(bytes: &[u8]) -> Result<(), SignatureError> {
    use SignatureErrorKind::*;

    if bytes.len() > u8::MAX as usize {
        return Err(SignatureError::new(SignatureTooLong));
    }

    let mut stack = Stack::<(Kind, u8), MAX_DEPTH>::new();
    let mut arrays = 0;
    let mut structs = 0;
    let mut n = 0;

    while n < bytes.len() {
        let b = bytes[n];
        n += 1;
        let t = Type::new(b);

        let mut is_basic = match t {
            Type::BYTE => true,
            Type::BOOLEAN => true,
            Type::INT16 => true,
            Type::UINT16 => true,
            Type::INT32 => true,
            Type::UINT32 => true,
            Type::INT64 => true,
            Type::UINT64 => true,
            Type::DOUBLE => true,
            Type::STRING => true,
            Type::OBJECT_PATH => true,
            Type::SIGNATURE => true,
            Type::VARIANT => true,
            Type::UNIX_FD => true,
            Type::ARRAY => {
                if !stack_try_push!(stack, (Kind::Array, 0)) || arrays == MAX_CONTAINER_DEPTH {
                    return Err(SignatureError::new(ExceededMaximumArrayRecursion));
                }

                arrays += 1;
                continue;
            }
            Type::OPEN_PAREN => {
                if !stack_try_push!(stack, (Kind::Struct, 0)) || structs == MAX_CONTAINER_DEPTH {
                    return Err(SignatureError::new(ExceededMaximumStructRecursion));
                }

                structs += 1;
                continue;
            }
            Type::CLOSE_PAREN => {
                let n = match stack_pop!(stack, (Kind, u8)) {
                    Some((Kind::Struct, n)) => n,
                    Some((Kind::Array, _)) => {
                        return Err(SignatureError::new(MissingArrayElementType));
                    }
                    _ => {
                        return Err(SignatureError::new(StructEndedButNotStarted));
                    }
                };

                if n == 0 {
                    return Err(SignatureError::new(StructHasNoFields));
                }

                structs -= 1;
                false
            }
            Type::OPEN_BRACE => {
                if !stack_try_push!(stack, (Kind::Dict, 0)) {
                    return Err(SignatureError::new(ExceededMaximumDictRecursion));
                }

                continue;
            }
            Type::CLOSE_BRACE => {
                let n = match stack_pop!(stack, (Kind, u8)) {
                    Some((Kind::Dict, n)) => n,
                    Some((Kind::Array, _)) => {
                        return Err(SignatureError::new(MissingArrayElementType));
                    }
                    _ => {
                        return Err(SignatureError::new(DictEndedButNotStarted));
                    }
                };

                match n {
                    0 => {
                        return Err(SignatureError::new(DictEntryHasNoFields));
                    }
                    1 => {
                        return Err(SignatureError::new(DictEntryHasOnlyOneField));
                    }
                    2 => {}
                    _ => {
                        return Err(SignatureError::new(DictEntryHasTooManyFields));
                    }
                }

                if !matches!(stack_peek!(stack), Some((Kind::Array, _))) {
                    return Err(SignatureError::new(DictEntryNotInsideArray));
                }

                false
            }
            t => return Err(SignatureError::new(UnknownTypeCode(t))),
        };

        while let Some((Kind::Array, _)) = stack_peek!(stack) {
            stack_pop!(stack, (Kind, u8));
            is_basic = false;
        }

        if let Some((Kind::Dict, 0)) = stack_peek!(stack)
            && !is_basic
        {
            return Err(SignatureError::new(DictKeyMustBeBasicType));
        }

        if let Some((kind, n)) = stack_pop!(stack, (Kind, u8)) {
            stack_try_push!(stack, (kind, n + 1));
        }
    }

    match stack_pop!(stack, (Kind, u8)) {
        Some((Kind::Array, _)) => {
            return Err(SignatureError::new(MissingArrayElementType));
        }
        Some((Kind::Struct, _)) => {
            return Err(SignatureError::new(StructStartedButNotEnded));
        }
        Some((Kind::Dict, _)) => {
            return Err(SignatureError::new(DictStartedButNotEnded));
        }
        _ => {}
    }

    Ok(())
}