use std::fmt;
use rama_utils::collections::smallvec::{SmallVec, smallvec};
use rama_utils::macros::enums::enum_builder;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PermissionsPolicyDirective {
pub name: PermissionsPolicyDirectiveName,
pub allow_list: SmallVec<[AllowlistSource; 4]>,
}
impl PermissionsPolicyDirective {
#[must_use]
pub fn deny(name: impl Into<PermissionsPolicyDirectiveName>) -> Self {
Self {
name: name.into(),
allow_list: SmallVec::new(),
}
}
#[must_use]
pub fn allow(name: impl Into<PermissionsPolicyDirectiveName>, source: AllowlistSource) -> Self {
Self {
name: name.into(),
allow_list: smallvec![source],
}
}
#[must_use]
pub fn allow_from(
name: impl Into<PermissionsPolicyDirectiveName>,
sources: impl IntoIterator<Item = AllowlistSource>,
) -> Self {
Self {
name: name.into(),
allow_list: sources.into_iter().collect(),
}
}
}
impl fmt::Display for PermissionsPolicyDirective {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}=(", self.name.as_str())?;
for (i, src) in self.allow_list.iter().enumerate() {
if i > 0 {
f.write_str(" ")?;
}
fmt::Display::fmt(src, f)?;
}
f.write_str(")")
}
}
enum_builder! {
@String
pub enum PermissionsPolicyDirectiveName {
Accelerometer => "accelerometer",
AmbientLightSensor => "ambient-light-sensor",
AttributionReporting => "attribution-reporting",
Autoplay => "autoplay",
Battery => "battery",
Bluetooth => "bluetooth",
BrowsingTopics => "browsing-topics",
Camera => "camera",
ClipboardRead => "clipboard-read",
ClipboardWrite => "clipboard-write",
ComputePressure => "compute-pressure",
CrossOriginIsolated => "cross-origin-isolated",
DisplayCapture => "display-capture",
EncryptedMedia => "encrypted-media",
Fullscreen => "fullscreen",
Gamepad => "gamepad",
Geolocation => "geolocation",
Gyroscope => "gyroscope",
Hid => "hid",
IdentityCredentialsGet => "identity-credentials-get",
IdleDetection => "idle-detection",
InterestCohort => "interest-cohort",
LocalFonts => "local-fonts",
Magnetometer => "magnetometer",
Microphone => "microphone",
Midi => "midi",
OtpCredentials => "otp-credentials",
Payment => "payment",
PictureInPicture => "picture-in-picture",
PublickeyCredentialsCreate => "publickey-credentials-create",
PublickeyCredentialsGet => "publickey-credentials-get",
ScreenWakeLock => "screen-wake-lock",
Serial => "serial",
SpeakerSelection => "speaker-selection",
StorageAccess => "storage-access",
SyncXhr => "sync-xhr",
Unload => "unload",
Usb => "usb",
WebShare => "web-share",
WindowManagement => "window-management",
XrSpatialTracking => "xr-spatial-tracking",
}
}
enum_builder! {
@String
pub enum AllowlistSource {
SelfOrigin => "self",
Wildcard => "*",
Src => "src",
}
}
impl AllowlistSource {
#[must_use]
pub fn origin(origin: impl AsRef<str>) -> Self {
Self::Unknown(format!("\"{}\"", origin.as_ref()).into())
}
pub(crate) fn from_token(s: &str) -> Option<Self> {
if let Some(inner) = s.strip_prefix('"').and_then(|t| t.strip_suffix('"')) {
if inner.is_empty() {
None
} else {
Some(Self::Unknown(format!("\"{inner}\"").into()))
}
} else {
match Self::from(s) {
Self::Unknown(_) => None,
parsed => Some(parsed),
}
}
}
}