feat(manifest): app manifest + decision crate (strict TOML subset, checked newtypes, capability catalog); docs/apps.md spec
This commit is contained in:
109
crates/manifest/src/capability.rs
Normal file
109
crates/manifest/src/capability.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
//! What an app can ask for: one name per app-facing server in the
|
||||
//! catalog (see the vision's Servers note). Whether this device has the
|
||||
//! server is a separate question, asked when an app is installed.
|
||||
|
||||
macro_rules! catalog {
|
||||
($($variant:ident = $name:literal,)*) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Capability {
|
||||
$($variant,)*
|
||||
}
|
||||
|
||||
impl Capability {
|
||||
pub const ALL: &[Capability] = &[$(Capability::$variant,)*];
|
||||
|
||||
pub fn from_name(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
$($name => Some(Capability::$variant),)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
$(Capability::$variant => $name,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
catalog! {
|
||||
Display = "display",
|
||||
Storage = "storage",
|
||||
AudioPlayback = "audio_playback",
|
||||
AudioCapture = "audio_capture",
|
||||
Camera = "camera",
|
||||
Network = "network",
|
||||
Telephony = "telephony",
|
||||
Bluetooth = "bluetooth",
|
||||
Gps = "gps",
|
||||
Usb = "usb",
|
||||
Power = "power",
|
||||
Notifications = "notifications",
|
||||
Clipboard = "clipboard",
|
||||
Contacts = "contacts",
|
||||
Calendar = "calendar",
|
||||
Alarms = "alarms",
|
||||
MediaIndex = "media_index",
|
||||
Keychain = "keychain",
|
||||
Sync = "sync",
|
||||
Accessibility = "accessibility",
|
||||
Print = "print",
|
||||
}
|
||||
|
||||
impl Capability {
|
||||
fn bit(self) -> u32 {
|
||||
1 << (self as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of capabilities.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct Capabilities(u32);
|
||||
|
||||
impl Capabilities {
|
||||
pub const NONE: Self = Self(0);
|
||||
|
||||
pub fn with(self, c: Capability) -> Self {
|
||||
Self(self.0 | c.bit())
|
||||
}
|
||||
|
||||
pub fn without(self, c: Capability) -> Self {
|
||||
Self(self.0 & !c.bit())
|
||||
}
|
||||
|
||||
pub fn contains(self, c: Capability) -> bool {
|
||||
self.0 & c.bit() != 0
|
||||
}
|
||||
|
||||
pub fn union(self, other: Self) -> Self {
|
||||
Self(self.0 | other.0)
|
||||
}
|
||||
|
||||
pub fn intersect(self, other: Self) -> Self {
|
||||
Self(self.0 & other.0)
|
||||
}
|
||||
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// Whether every capability in `self` is in `other`.
|
||||
pub fn is_subset(self, other: Self) -> bool {
|
||||
self.0 & !other.0 == 0
|
||||
}
|
||||
|
||||
pub fn iter(self) -> impl Iterator<Item = Capability> {
|
||||
Capability::ALL
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(move |c| self.contains(*c))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Capability> for Capabilities {
|
||||
fn from_iter<I: IntoIterator<Item = Capability>>(iter: I) -> Self {
|
||||
iter.into_iter().fold(Self::NONE, Self::with)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user