blob: 12451879cbf22da2c5c09037535e40b453e1e06c [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
pub mod error;
pub mod index;
pub mod metadata;
pub mod parsed_policy;
pub mod parser;
mod arrays;
mod extensible_bitmap;
mod security_context;
mod symbols;
pub use security_context::{SecurityContext, SecurityContextError};
use {
anyhow::Context as _,
error::{NewSecurityContextError, ParseError, QueryError},
index::PolicyIndex,
metadata::HandleUnknown,
parsed_policy::ParsedPolicy,
parser::ByValue,
parser::{ByRef, ParseStrategy},
selinux_common::{self as sc, ClassPermission as _, FileClass},
std::{fmt::Debug, marker::PhantomData, num::NonZeroU32, ops::Deref},
zerocopy::{little_endian as le, ByteSlice, FromBytes, NoCell, Ref, Unaligned},
};
/// Maximum SELinux policy version supported by this implementation.
pub const SUPPORTED_POLICY_VERSION: u32 = 33;
/// Identifies a user within a policy.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct UserId(NonZeroU32);
/// Identifies a role within a policy.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct RoleId(NonZeroU32);
/// Identifies a type within a policy.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct TypeId(NonZeroU32);
/// Identifies a sensitivity level within a policy.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct SensitivityId(NonZeroU32);
/// Identifies a security category within a policy.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct CategoryId(NonZeroU32);
/// The set of permissions that may be granted to sources accessing targets of a particular class,
/// as defined in an SELinux policy.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AccessVector(u32);
impl AccessVector {
pub const NONE: AccessVector = AccessVector(0);
pub const ALL: AccessVector = AccessVector(std::u32::MAX);
pub(crate) fn from_raw(access_vector: u32) -> Self {
Self(access_vector)
}
#[cfg(feature = "selinux_policy_test_api")]
pub fn into_raw(self) -> u32 {
self.0
}
}
impl std::ops::BitAnd for AccessVector {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
AccessVector(self.0 & rhs.0)
}
}
impl std::ops::BitOr for AccessVector {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
AccessVector(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for AccessVector {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0
}
}
/// Parses `binary_policy` by value; that is, copies underlying binary data out in addition to
/// building up parser output structures. This function returns
/// `(unvalidated_parser_output, binary_policy)` on success, or an error if parsing failed. Note
/// that the second component of the success case contains precisely the same bytes as the input.
/// This function depends on a uniformity of interface between the "by value" and "by reference"
/// strategies, but also requires an `unvalidated_parser_output` type that is independent of the
/// `binary_policy` lifetime. Taken together, these requirements demand the "move-in + move-out"
/// interface for `binary_policy`.
///
/// If the caller does not need access to the binary policy when parsing fails, but does need to
/// retain both the parsed output and the binary policy when parsing succeeds, the code will look
/// something like:
///
/// ```rust,ignore
/// let (unvalidated_policy, binary_policy) = parse_policy_by_value(binary_policy)?;
/// ```
///
/// If the caller does need access to the binary policy when parsing fails and needs to retain both
/// parsed output and the binary policy when parsing succeeds, the code will look something like:
///
/// ```rust,ignore
/// let (unvalidated_policy, _) = parse_policy_by_value(binary_policy.clone())?;
/// ```
///
/// If the caller does not need to retain both the parsed output and the binary policy, then
/// [`parse_policy_by_reference`] should be used instead.
pub fn parse_policy_by_value(
binary_policy: Vec<u8>,
) -> Result<(Unvalidated<ByValue<Vec<u8>>>, Vec<u8>), anyhow::Error> {
let (parsed_policy, binary_policy) =
ParsedPolicy::parse(ByValue::new(binary_policy)).context("parsing policy")?;
Ok((Unvalidated(parsed_policy), binary_policy))
}
/// Parses `binary_policy` by reference; that is, constructs parser output structures that contain
/// _references_ to data in `binary_policy`. This function returns `unvalidated_parser_output` on
/// success, or an error if parsing failed.
///
/// If the caller does needs to retain both the parsed output and the binary policy, then
/// [`parse_policy_by_value`] should be used instead.
pub fn parse_policy_by_reference<'a>(
binary_policy: &'a [u8],
) -> Result<Unvalidated<ByRef<&'a [u8]>>, anyhow::Error> {
let (parsed_policy, _) =
ParsedPolicy::parse(ByRef::new(binary_policy)).context("parsing policy")?;
Ok(Unvalidated(parsed_policy))
}
#[derive(Debug)]
pub struct Policy<PS: ParseStrategy>(PolicyIndex<PS>);
impl<PS: ParseStrategy> Policy<PS> {
/// The policy version stored in the underlying binary policy.
pub fn policy_version(&self) -> u32 {
self.0.parsed_policy().policy_version()
}
/// The way "unknown" policy decisions should be handed according to the underlying binary
/// policy.
pub fn handle_unknown(&self) -> &HandleUnknown {
self.0.parsed_policy().handle_unknown()
}
pub fn conditional_booleans<'a>(&'a self) -> Vec<(&'a [u8], bool)> {
self.0
.parsed_policy()
.conditional_booleans()
.iter()
.map(|boolean| (PS::deref_slice(&boolean.data), PS::deref(&boolean.metadata).active()))
.collect()
}
/// Returns the [`SecurityContext`] defined by this policy for the specified
/// well-known (or "initial") Id.
///
/// # Panics
///
/// If the policy is not internally consistent, such that e.g. the Context refers to
/// user, role, etc Ids that the policy does not define. This indicates that there is
/// some missing validation of policy fields.
pub fn initial_context(&self, id: sc::InitialSid) -> security_context::SecurityContext {
let id = le::U32::from(id as u32);
// Policy validation is assumed to have ensured that all `InitialSid` values exist
// and have valid & consistent content.
let context = self.0.parsed_policy().initial_context(id).unwrap();
let low_level = self.0.security_level(context.low_level());
let high_level = context.high_level().as_ref().map(|x| self.0.security_level(x));
security_context::SecurityContext::new(
&self.0,
context.user_id(),
context.role_id(),
context.type_id(),
low_level,
high_level,
)
.unwrap()
}
/// Returns a [`SecurityContext`] with fields parsed from the supplied Security Context string.
pub fn parse_security_context(
&self,
security_context: &[u8],
) -> Result<security_context::SecurityContext, security_context::SecurityContextError> {
security_context::SecurityContext::parse(&self.0, security_context)
}
/// Returns a byte string describing the supplied [`SecurityContext`].
pub fn serialize_security_context(&self, security_context: &SecurityContext) -> Vec<u8> {
security_context.serialize(&self.0)
}
/// Returns the security context that should be applied to a newly created file-like SELinux
/// object according to `source` and `target` security contexts, as well as the new object's
/// `class`. Returns an error if the security context for such an object is not well-defined
/// by this [`Policy`].
pub fn new_file_security_context(
&self,
source: &SecurityContext,
target: &SecurityContext,
class: &FileClass,
) -> Result<SecurityContext, NewSecurityContextError> {
self.0.new_file_security_context(source, target, class)
}
/// Returns whether the input types are explicitly granted `permission` via an `allow [...];`
/// policy statement.
///
/// # Panics
/// If supplied with type Ids not previously obtained from the `Policy` itself; validation
/// ensures that all such Ids have corresponding definitions.
pub fn is_explicitly_allowed(
&self,
source_type: TypeId,
target_type: TypeId,
permission: sc::Permission,
) -> Result<bool, QueryError> {
let object_class = permission.class();
let target_class = self.0.class(&object_class);
let permission = self.0.permission(&permission);
self.0.parsed_policy().class_permission_is_explicitly_allowed(
source_type,
target_type,
target_class,
permission,
)
}
/// Returns whether the input types are explicitly granted the permission named
/// `permission_name` via an `allow [...];` policy statement, or an error if looking up the
/// input types fails. This is the "custom" form of this API because `permission_name` is
/// associated with a [`selinux_common::AbstractPermission::Custom::permission`] value.
///
/// # Panics
/// If supplied with type Ids not previously obtained from the `Policy` itself; validation
/// ensures that all such Ids have corresponding definitions.
pub fn is_explicitly_allowed_custom(
&self,
source_type: TypeId,
target_type: TypeId,
target_class_name: &str,
permission_name: &str,
) -> Result<bool, QueryError> {
self.0.parsed_policy().is_explicitly_allowed_custom(
source_type,
target_type,
target_class_name,
permission_name,
)
}
/// Computes the access vector that associates type `source_type_name` and `target_type_name`
/// via an explicit `allow [...];` statement in the binary policy. Computes `AccessVector::NONE`
/// if no such statement exists.
pub fn compute_explicitly_allowed(
&self,
source_type: TypeId,
target_type: TypeId,
object_class: sc::ObjectClass,
) -> Result<AccessVector, QueryError> {
let target_class = self.0.class(&object_class);
self.0.parsed_policy().compute_explicitly_allowed(source_type, target_type, target_class)
}
/// Computes the access vector that associates type `source_type_name` and `target_type_name`
/// via an explicit `allow [...];` statement in the binary policy. Computes `AccessVector::NONE`
/// if no such statement exists. This is the "custom" form of this API because
/// `target_class_name` is associated with a [`selinux_common::AbstractObjectClass::Custom`]
/// value.
pub fn compute_explicitly_allowed_custom(
&self,
source_type: TypeId,
target_type: TypeId,
target_class_name: &str,
) -> Result<AccessVector, QueryError> {
self.0.parsed_policy().compute_explicitly_allowed_custom(
source_type,
target_type,
target_class_name,
)
}
#[cfg(feature = "selinux_policy_test_api")]
pub fn print_permissions(&self) {
let parsed_policy = self.0.parsed_policy();
for class in parsed_policy.classes().into_iter() {
println!("{}", std::str::from_utf8(class.name_bytes()).expect("class name"));
for permission in class.permissions().into_iter() {
println!(
" {}",
std::str::from_utf8(permission.name_bytes()).expect("permission name")
);
}
}
}
#[cfg(feature = "selinux_policy_test_api")]
pub fn type_id_by_name(&self, name: &str) -> TypeId {
self.0.parsed_policy().type_id_by_name(name)
}
}
impl<PS: ParseStrategy> AccessVectorComputer for Policy<PS> {
fn access_vector_from_permission<P: sc::ClassPermission + Into<sc::Permission> + 'static>(
&self,
permission: P,
) -> AccessVector {
let permission = self.0.permission(&permission.into());
// Compute bit flag associated with permission.
// Use `permission.id() - 1` below because ids start at `1` to refer to the
// "shift `1` by 0 bits".
//
// id=1 => bits:0...001, id=2 => bits:0...010, etc.
AccessVector(1 << (permission.id() - 1))
}
fn access_vector_from_permissions<
'a,
P: sc::ClassPermission + Into<sc::Permission> + 'static,
PI: IntoIterator<Item = P>,
>(
&self,
permissions: PI,
) -> AccessVector {
let mut access_vector = AccessVector::NONE;
for permission in permissions.into_iter() {
access_vector |= self.access_vector_from_permission(permission);
}
access_vector
}
}
impl<PS: ParseStrategy> Validate for Policy<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
self.0.parsed_policy().validate()
}
}
/// A [`Policy`] that has been successfully parsed, but not validated.
pub struct Unvalidated<PS: ParseStrategy>(ParsedPolicy<PS>);
impl<PS: ParseStrategy> Unvalidated<PS> {
pub fn validate(self) -> Result<Policy<PS>, anyhow::Error> {
Validate::validate(&self.0).context("validating parsed policy")?;
let index = PolicyIndex::new(self.0).context("building index")?;
Ok(Policy(index))
}
#[cfg(feature = "selinux_policy_test_api")]
pub fn parsed_policy(&self) -> &ParsedPolicy<PS> {
&self.0
}
}
/// An owner of policy information that can translate [`sc::Permission`] values into
/// [`AccessVector`] values that are consistent with the owned policy.
pub trait AccessVectorComputer {
/// Returns an [`AccessVector`] with a single bit set that corresponds to `permission`.
fn access_vector_from_permission<P: sc::ClassPermission + Into<sc::Permission> + 'static>(
&self,
permission: P,
) -> AccessVector;
/// Computes an [`AccessVector`] where the only bits set are those that correspond to
/// all `permissions`. This operation fails if `permissions` contain permissions that refer to
/// different object classes because an access vector specifies permission bits associated with
/// one specific object class.
fn access_vector_from_permissions<
'a,
P: sc::ClassPermission + Into<sc::Permission> + 'static,
PI: IntoIterator<Item = P>,
>(
&self,
permissions: PI,
) -> AccessVector;
}
/// A data structure that can be parsed as a part of a binary policy.
pub trait Parse<PS: ParseStrategy>: Sized {
/// The type of error that may be returned from `parse()`, usually [`ParseError`] or
/// [`anyhow::Error`].
type Error: Into<anyhow::Error>;
/// Parses a `Self` from `bytes`, returning the `Self` and trailing bytes, or an error if
/// bytes corresponding to a `Self` are malformed.
fn parse(bytes: PS) -> Result<(Self, PS), Self::Error>;
}
/// Parse a data as a slice of inner data structures from a prefix of a [`ByteSlice`].
pub(crate) trait ParseSlice<PS: ParseStrategy>: Sized {
/// The type of error that may be returned from `parse()`, usually [`ParseError`] or
/// [`anyhow::Error`].
type Error: Into<anyhow::Error>;
/// Parses a `Self` as `count` of internal itemsfrom `bytes`, returning the `Self` and trailing
/// bytes, or an error if bytes corresponding to a `Self` are malformed.
fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error>;
}
/// Validate a parsed data structure.
pub(crate) trait Validate {
/// The type of error that may be returned from `validate()`, usually [`ParseError`] or
/// [`anyhow::Error`].
type Error: Into<anyhow::Error>;
/// Validates a `Self`, returning a `Self::Error` if `self` is internally inconsistent.
fn validate(&self) -> Result<(), Self::Error>;
}
pub(crate) trait ValidateArray<M, D> {
/// The type of error that may be returned from `validate()`, usually [`ParseError`] or
/// [`anyhow::Error`].
type Error: Into<anyhow::Error>;
/// Validates a `Self`, returning a `Self::Error` if `self` is internally inconsistent.
fn validate_array<'a>(metadata: &'a M, data: &'a [D]) -> Result<(), Self::Error>;
}
/// Treat a type as metadata that contains a count of subsequent data.
pub(crate) trait Counted {
/// Returns the count of subsequent data items.
fn count(&self) -> u32;
}
impl<T: Validate> Validate for Option<T> {
type Error = <T as Validate>::Error;
fn validate(&self) -> Result<(), Self::Error> {
match self {
Some(value) => value.validate(),
None => Ok(()),
}
}
}
impl Validate for le::U32 {
type Error = anyhow::Error;
/// Using a raw `le::U32` implies no additional constraints on its value. To operate with
/// constraints, define a `struct T(le::U32);` and `impl Validate for T { ... }`.
fn validate(&self) -> Result<(), Self::Error> {
Ok(())
}
}
impl Validate for u8 {
type Error = anyhow::Error;
/// Using a raw `u8` implies no additional constraints on its value. To operate with
/// constraints, define a `struct T(u8);` and `impl Validate for T { ... }`.
fn validate(&self) -> Result<(), Self::Error> {
Ok(())
}
}
impl Validate for [u8] {
type Error = anyhow::Error;
/// Using a raw `[u8]` implies no additional constraints on its value. To operate with
/// constraints, define a `struct T([u8]);` and `impl Validate for T { ... }`.
fn validate(&self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<B: ByteSlice, T: Validate + FromBytes + NoCell> Validate for Ref<B, T> {
type Error = <T as Validate>::Error;
fn validate(&self) -> Result<(), Self::Error> {
self.deref().validate()
}
}
impl<B: ByteSlice, T: Counted + FromBytes + NoCell> Counted for Ref<B, T> {
fn count(&self) -> u32 {
self.deref().count()
}
}
/// A length-encoded array that contains metadata in `M` and a slice of data items internally
/// managed by `D`.
#[derive(Clone, Debug, PartialEq)]
struct Array<PS, M, D> {
metadata: M,
data: D,
_marker: PhantomData<PS>,
}
impl<PS: ParseStrategy, M: Counted + Parse<PS>, D: ParseSlice<PS>> Parse<PS> for Array<PS, M, D> {
/// [`Array`] abstracts over two types (`M` and `D`) that may have different [`Parse::Error`]
/// types. Unify error return type via [`anyhow::Error`].
type Error = anyhow::Error;
/// Parses [`Array`] by parsing *and validating* `metadata`, `data`, and `self`.
fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
let tail = bytes;
let (metadata, tail) = M::parse(tail).map_err(Into::<anyhow::Error>::into)?;
let (data, tail) =
D::parse_slice(tail, metadata.count() as usize).map_err(Into::<anyhow::Error>::into)?;
let array = Self { metadata, data, _marker: PhantomData };
Ok((array, tail))
}
}
impl<
T: Clone + Debug + FromBytes + NoCell + PartialEq + Unaligned,
PS: ParseStrategy<Output<T> = T>,
> Parse<PS> for T
{
type Error = anyhow::Error;
fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
let num_bytes = bytes.len();
let (data, tail) = PS::parse::<T>(bytes).ok_or(ParseError::MissingData {
type_name: std::any::type_name::<T>(),
type_size: std::mem::size_of::<T>(),
num_bytes,
})?;
Ok((data, tail))
}
}
/// Defines a at type that wraps an [`Array`], implementing `Deref`-as-`Array` and [`Parse`]. This
/// macro should be used in contexts where using a general [`Array`] implementation may introduce
/// conflicting implementations on account of general [`Array`] type parameters.
macro_rules! array_type {
($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty, $metadata_type_name:expr, $data_type_name:expr) => {
#[doc = "An [`Array`] with [`"]
#[doc = $metadata_type_name]
#[doc = "`] metadata and [`"]
#[doc = $data_type_name]
#[doc = "`] data items."]
#[derive(Debug, PartialEq)]
pub(crate) struct $type_name<$parse_strategy: crate::parser::ParseStrategy>(
crate::Array<PS, $metadata_type, $data_type>,
);
impl<PS: crate::parser::ParseStrategy> std::ops::Deref for $type_name<PS> {
type Target = crate::Array<PS, $metadata_type, $data_type>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<PS: crate::parser::ParseStrategy> crate::Parse<PS> for $type_name<PS>
where
Array<PS, $metadata_type, $data_type>: crate::Parse<PS>,
{
type Error = <Array<PS, $metadata_type, $data_type> as crate::Parse<PS>>::Error;
fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
let (array, tail) = Array::<PS, $metadata_type, $data_type>::parse(bytes)?;
Ok((Self(array), tail))
}
}
};
($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty) => {
array_type!(
$type_name,
$parse_strategy,
$metadata_type,
$data_type,
stringify!($metadata_type),
stringify!($data_type)
);
};
}
pub(crate) use array_type;
macro_rules! array_type_validate_deref_both {
($type_name:ident) => {
impl<PS: crate::parser::ParseStrategy> Validate for $type_name<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
let metadata = PS::deref(&self.metadata);
metadata.validate()?;
let data = PS::deref_slice(&self.data);
data.validate()?;
Self::validate_array(metadata, data).map_err(Into::<anyhow::Error>::into)
}
}
};
}
pub(crate) use array_type_validate_deref_both;
macro_rules! array_type_validate_deref_data {
($type_name:ident) => {
impl<PS: crate::parser::ParseStrategy> Validate for $type_name<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
let metadata = &self.metadata;
metadata.validate()?;
let data = PS::deref_slice(&self.data);
data.validate()?;
Self::validate_array(metadata, data)
}
}
};
}
pub(crate) use array_type_validate_deref_data;
macro_rules! array_type_validate_deref_metadata_data_vec {
($type_name:ident) => {
impl<PS: crate::parser::ParseStrategy> Validate for $type_name<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
let metadata = PS::deref(&self.metadata);
metadata.validate()?;
let data = &self.data;
data.validate()?;
Self::validate_array(metadata, data.as_slice())
}
}
};
}
pub(crate) use array_type_validate_deref_metadata_data_vec;
macro_rules! array_type_validate_deref_none_data_vec {
($type_name:ident) => {
impl<PS: crate::parser::ParseStrategy> Validate for $type_name<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
let metadata = &self.metadata;
metadata.validate()?;
let data = &self.data;
data.validate()?;
Self::validate_array(metadata, data.as_slice())
}
}
};
}
pub(crate) use array_type_validate_deref_none_data_vec;
impl<
B: Debug + ByteSlice + PartialEq,
T: Clone + Debug + FromBytes + NoCell + PartialEq + Unaligned,
> Parse<ByRef<B>> for Ref<B, T>
{
type Error = anyhow::Error;
fn parse(bytes: ByRef<B>) -> Result<(Self, ByRef<B>), Self::Error> {
let num_bytes = bytes.len();
let (data, tail) = ByRef::<B>::parse::<T>(bytes).ok_or(ParseError::MissingData {
type_name: std::any::type_name::<T>(),
type_size: std::mem::size_of::<T>(),
num_bytes,
})?;
Ok((data, tail))
}
}
impl<
B: Debug + ByteSlice + PartialEq,
T: Clone + Debug + FromBytes + NoCell + PartialEq + Unaligned,
> ParseSlice<ByRef<B>> for Ref<B, [T]>
{
/// [`Ref`] may return a [`ParseError`] internally, or `<T as Parse>::Error`. Unify error return
/// type via [`anyhow::Error`].
type Error = anyhow::Error;
/// Parses [`Ref`] by consuming it as an unaligned prefix as a slice, then validating the slice
/// via `Ref::deref`.
fn parse_slice(bytes: ByRef<B>, count: usize) -> Result<(Self, ByRef<B>), Self::Error> {
let num_bytes = bytes.len();
let (data, tail) =
ByRef::<B>::parse_slice::<T>(bytes, count).ok_or(ParseError::MissingSliceData {
type_name: std::any::type_name::<T>(),
type_size: std::mem::size_of::<T>(),
num_items: count,
num_bytes,
})?;
Ok((data, tail))
}
}
impl<PS: ParseStrategy, T: Parse<PS>> ParseSlice<PS> for Vec<T> {
/// `Vec<T>` may return a [`ParseError`] internally, or `<T as Parse>::Error`. Unify error
/// return type via [`anyhow::Error`].
type Error = anyhow::Error;
/// Parses `Vec<T>` by parsing individual `T` instances, then validating them.
fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error> {
let mut slice = Vec::with_capacity(count);
let mut tail = bytes;
for _ in 0..count {
let (item, next_tail) = T::parse(tail).map_err(Into::<anyhow::Error>::into)?;
slice.push(item);
tail = next_tail;
}
Ok((slice, tail))
}
}
pub mod testing {
use super::AccessVector;
pub const ACCESS_VECTOR_0001: AccessVector = AccessVector(0b0001u32);
pub const ACCESS_VECTOR_0010: AccessVector = AccessVector(0b0010u32);
pub const ACCESS_VECTOR_0100: AccessVector = AccessVector(0b0100u32);
pub const ACCESS_VECTOR_1000: AccessVector = AccessVector(0b1000u32);
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
use super::error::ValidateError;
/// Downcasts an [`anyhow::Error`] to a [`ParseError`] for structured error comparison in tests.
pub fn as_parse_error(error: anyhow::Error) -> ParseError {
error.downcast::<ParseError>().expect("parse error")
}
/// Downcasts an [`anyhow::Error`] to a [`ParseError`] for structured error comparison in tests.
pub fn as_validate_error(error: anyhow::Error) -> ValidateError {
error.downcast::<ValidateError>().expect("validate error")
}
macro_rules! parse_test {
($parse_output:ident, $data:expr, $result:tt, $check_impl:block) => {{
let data = $data;
fn check_by_ref<'a>(
$result: Result<
($parse_output<ByRef<&'a [u8]>>, ByRef<&'a [u8]>),
<$parse_output<ByRef<&'a [u8]>> as crate::Parse<ByRef<&'a [u8]>>>::Error,
>,
) {
$check_impl;
}
fn check_by_value(
$result: Result<
($parse_output<ByValue<Vec<u8>>>, ByValue<Vec<u8>>),
<$parse_output<ByValue<Vec<u8>>> as crate::Parse<ByValue<Vec<u8>>>>::Error,
>,
) -> Option<($parse_output<ByValue<Vec<u8>>>, ByValue<Vec<u8>>)> {
$check_impl
}
let by_ref = ByRef::new(data.as_slice());
let by_ref_result = $parse_output::parse(by_ref);
check_by_ref(by_ref_result);
let by_value_result = $parse_output::<ByValue<Vec<u8>>>::parse(ByValue::new(data));
let _ = check_by_value(by_value_result);
}};
}
pub(crate) use parse_test;
macro_rules! validate_test {
($parse_output:ident, $data:expr, $result:tt, $check_impl:block) => {{
let data = $data;
fn check_by_ref<'a>(
$result: Result<(), <$parse_output<ByRef<&'a [u8]>> as crate::Validate>::Error>,
) {
$check_impl;
}
fn check_by_value(
$result: Result<(), <$parse_output<ByValue<Vec<u8>>> as crate::Validate>::Error>,
) {
$check_impl
}
let by_ref = ByRef::new(data.as_slice());
let (by_ref_parsed, _) =
$parse_output::parse(by_ref).expect("successful parse for validate test");
let by_ref_result = by_ref_parsed.validate();
check_by_ref(by_ref_result);
let (by_value_parsed, _) = $parse_output::<ByValue<Vec<u8>>>::parse(ByValue::new(data))
.expect("successful parse for validate test");
let by_value_result = by_value_parsed.validate();
check_by_value(by_value_result);
}};
}
pub(crate) use validate_test;
}