use std::num::NonZeroU32;
use winnow::{
Parser,
combinator::{dispatch, eof, preceded, repeat_till},
stream::{Location, Stream},
token::{any, take},
};
use zbus_utils::{
names::{self, BusNameKind},
object_path,
};
use super::{
ByteOrder, Input, PResult, ParseError, QuickFields,
quick_fields::{Field, FieldPos},
};
use crate::{Signature, message::FieldCode};
pub(super) fn fields<O: ByteOrder>(input: &mut Input<'_>) -> PResult<QuickFields> {
repeat_till(0.., preceded(padding, field::<O>), eof)
.map(|(fields, _)| fields)
.parse_next(input)
}
#[inline(always)]
fn padding(input: &mut Input<'_>) -> PResult<()> {
let len = input.current_token_start().wrapping_neg() % 8;
take(len)
.try_map(
|padding: &[u8]| match padding.iter().find(|byte| **byte != 0) {
Some(byte) => Err(ParseError::PaddingNot0(*byte)),
None => Ok(()),
},
)
.parse_next(input)
}
#[inline(always)]
fn field<O: ByteOrder>(input: &mut Input<'_>) -> PResult<Field> {
dispatch! {(any, variant_signature);
(PATH, b'o') => path::<O>.map(Field::Path),
(INTERFACE, b's') => name::<O, _>(names::validate_interface_name).map(Field::Interface),
(MEMBER, b's') => name::<O, _>(names::validate_member_name).map(Field::Member),
(ERROR_NAME, b's') => name::<O, _>(names::validate_error_name).map(Field::ErrorName),
(REPLY_SERIAL, b'u') => O::u32.try_map(reply_serial).map(Field::ReplySerial),
(DESTINATION, b's') => bus_name::<O>.map(|(pos, unique)| Field::Destination { pos, unique }),
(SENDER, b's') => name::<O, _>(names::validate_unique_name).map(Field::Sender),
(SIGNATURE, b'g') => body_signature,
(UNIX_FDS, b'u') => O::u32.map(Field::UnixFds),
_ => unknown,
}
.parse_next(input)
}
#[inline(always)]
fn variant_signature(input: &mut Input<'_>) -> PResult<u8> {
take(3usize)
.try_map(|signature: &[u8]| match signature {
[1, value_type, 0] => Ok(*value_type),
_ => Err(ParseError::Field),
})
.parse_next(input)
}
#[inline(always)]
fn path<O: ByteOrder>(input: &mut Input<'_>) -> PResult<FieldPos> {
string::<O>
.try_map(|(pos, path)| match object_path::validate(path) {
Ok(()) => Ok(pos),
Err(_) => Err(ParseError::ObjectPath),
})
.parse_next(input)
}
#[inline(always)]
fn name<'i, O, V>(validate: V) -> impl Parser<Input<'i>, FieldPos, ParseError>
where
O: ByteOrder,
V: Fn(&[u8]) -> Result<(), &'static str>,
{
string::<O>.try_map(move |(pos, name)| validate(name).map(|()| pos).map_err(ParseError::Name))
}
#[inline(always)]
fn bus_name<O: ByteOrder>(input: &mut Input<'_>) -> PResult<(FieldPos, bool)> {
string::<O>
.try_map(|(pos, name)| match names::validate_bus_name(name) {
Ok(kind) => Ok((pos, kind == BusNameKind::Unique)),
Err(reason) => Err(ParseError::Name(reason)),
})
.parse_next(input)
}
#[inline(always)]
fn string<'i, O: ByteOrder>(input: &mut Input<'i>) -> PResult<(FieldPos, &'i [u8])> {
let len = O::u32(input)?;
nul_terminated(input, len as usize)
}
fn reply_serial(serial: u32) -> Result<NonZeroU32, ParseError> {
NonZeroU32::new(serial).ok_or(ParseError::Field)
}
#[inline(always)]
fn body_signature(input: &mut Input<'_>) -> PResult<Field> {
let len = any(input)?;
let (pos, signature) = nul_terminated(input, len.into())?;
Signature::from_bytes(signature)
.map(|signature| Field::signature(signature, pos))
.map_err(|_| ParseError::Signature)
}
#[inline(always)]
fn nul_terminated<'i>(input: &mut Input<'i>, len: usize) -> PResult<(FieldPos, &'i [u8])> {
if input.eof_offset() <= len {
return Err(ParseError::Truncated);
}
let start = input.current_token_start();
match input.next_slice(len + 1).split_last() {
Some((0, bytes)) => Ok((FieldPos::new(start, start + len), bytes)),
_ => Err(ParseError::Field),
}
}
fn unknown(_input: &mut Input<'_>) -> PResult<Field> {
Err(ParseError::Field)
}
const PATH: u8 = FieldCode::Path as u8;
const INTERFACE: u8 = FieldCode::Interface as u8;
const MEMBER: u8 = FieldCode::Member as u8;
const ERROR_NAME: u8 = FieldCode::ErrorName as u8;
const REPLY_SERIAL: u8 = FieldCode::ReplySerial as u8;
const DESTINATION: u8 = FieldCode::Destination as u8;
const SENDER: u8 = FieldCode::Sender as u8;
const SIGNATURE: u8 = FieldCode::Signature as u8;
const UNIX_FDS: u8 = FieldCode::UnixFDs as u8;