use crate::cap::{Cap, SendCap};
use crate::permission::*;
pub trait Has<P: Permission> {
fn cap_ref(&self) -> Cap<P>;
}
impl<P: Permission> Has<P> for Cap<P> {
fn cap_ref(&self) -> Cap<P> {
Cap::new()
}
}
impl<P: Permission> Has<P> for SendCap<P> {
fn cap_ref(&self) -> Cap<P> {
self.as_cap()
}
}
macro_rules! impl_subsumes {
($super:ty => $($sub:ty),+) => {
$(
impl Has<$sub> for Cap<$super> {
fn cap_ref(&self) -> Cap<$sub> { Cap::new() }
}
)+
}
}
impl_subsumes!(FsAll => FsRead, FsWrite);
impl_subsumes!(NetAll => NetConnect, NetBind);
macro_rules! impl_ambient {
($($perm:ty),+) => {
$(
impl Has<$perm> for Cap<Ambient> {
fn cap_ref(&self) -> Cap<$perm> { Cap::new() }
}
)+
}
}
impl_ambient!(
FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn
);
macro_rules! impl_tuple_has_first {
([$($a:ident),+]; $all:tt) => {
$( impl_tuple_has_first!(@inner $a; $all); )+
};
(@inner $a:ident; [$($b:ident),+]) => {
$(
impl Has<$a> for Cap<($a, $b)> {
fn cap_ref(&self) -> Cap<$a> { Cap::new() }
}
)+
};
}
macro_rules! impl_tuple_has_second {
($first:ident $(, $rest:ident)+) => {
$(
impl Has<$first> for Cap<($rest, $first)> {
fn cap_ref(&self) -> Cap<$first> { Cap::new() }
}
impl Has<$rest> for Cap<($first, $rest)> {
fn cap_ref(&self) -> Cap<$rest> { Cap::new() }
}
)+
impl_tuple_has_second!($($rest),+);
};
($single:ident) => {};
}
impl_tuple_has_first!(
[FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient];
[FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient]
);
impl_tuple_has_second!(
FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient
);
#[cfg(test)]
mod tests {
use super::*;
use crate::root::test_root;
#[test]
fn direct_cap_satisfies_has() {
let root = test_root();
let cap = root.grant::<FsRead>();
fn needs_fs(_: &impl Has<FsRead>) {}
needs_fs(&cap);
}
#[test]
fn fs_all_subsumes_read_and_write() {
let root = test_root();
let cap = root.grant::<FsAll>();
fn needs_read(_: &impl Has<FsRead>) {}
fn needs_write(_: &impl Has<FsWrite>) {}
needs_read(&cap);
needs_write(&cap);
}
#[test]
fn net_all_subsumes_connect_and_bind() {
let root = test_root();
let cap = root.grant::<NetAll>();
fn needs_connect(_: &impl Has<NetConnect>) {}
fn needs_bind(_: &impl Has<NetBind>) {}
needs_connect(&cap);
needs_bind(&cap);
}
#[test]
fn ambient_satisfies_anything() {
let root = test_root();
let cap = root.grant::<Ambient>();
fn needs_fs(_: &impl Has<FsRead>) {}
fn needs_net(_: &impl Has<NetConnect>) {}
fn needs_spawn(_: &impl Has<Spawn>) {}
needs_fs(&cap);
needs_net(&cap);
needs_spawn(&cap);
}
#[test]
fn multiple_cap_params() {
fn sync_data(_fs: &impl Has<FsRead>, _net: &impl Has<NetConnect>) {}
let root = test_root();
let fs = root.grant::<FsRead>();
let net = root.grant::<NetConnect>();
sync_data(&fs, &net);
}
#[test]
fn tuple_cap_satisfies_both_has() {
let root = test_root();
let cap = root.grant::<(FsRead, NetConnect)>();
fn needs_fs(_: &impl Has<FsRead>) {}
fn needs_net(_: &impl Has<NetConnect>) {}
needs_fs(&cap);
needs_net(&cap);
}
#[test]
fn tuple_self_pair() {
let root = test_root();
let cap = root.grant::<(FsRead, FsRead)>();
fn needs_fs(_: &impl Has<FsRead>) {}
needs_fs(&cap);
}
#[test]
fn tuple_with_subsumption_type() {
let root = test_root();
let cap = root.grant::<(FsAll, NetConnect)>();
fn needs_fs_all(_: &impl Has<FsAll>) {}
fn needs_net(_: &impl Has<NetConnect>) {}
needs_fs_all(&cap);
needs_net(&cap);
}
#[test]
fn tuple_cap_ref_returns_correct_type() {
let root = test_root();
let cap = root.grant::<(FsRead, NetConnect)>();
let _fs: Cap<FsRead> = Has::<FsRead>::cap_ref(&cap);
let _net: Cap<NetConnect> = Has::<NetConnect>::cap_ref(&cap);
}
#[test]
fn tuple_is_zst() {
use std::mem::size_of;
assert_eq!(size_of::<Cap<(FsRead, NetConnect)>>(), 0);
}
#[test]
fn ambient_covers_all_permissions() {
fn assert_ambient_has<P: Permission>()
where
Cap<Ambient>: Has<P>,
{
}
assert_ambient_has::<FsRead>();
assert_ambient_has::<FsWrite>();
assert_ambient_has::<FsAll>();
assert_ambient_has::<NetConnect>();
assert_ambient_has::<NetBind>();
assert_ambient_has::<NetAll>();
assert_ambient_has::<EnvRead>();
assert_ambient_has::<EnvWrite>();
assert_ambient_has::<Spawn>();
}
}