krystv's picture
Upload 107 files
3374e90 verified
Raw
History Blame Contribute Delete
3.05 kB
use crate::capability::Capabilities;
use crate::error::BexError;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Manifest {
pub schema: u32,
pub id: String,
pub name: String,
pub version: String,
pub authors: Vec<String>,
pub abi: String,
pub provides: ProvidesSpec,
pub network: NetworkSpec,
pub storage: bool,
#[serde(default)]
pub secrets: Vec<String>,
/// Whether the plugin is allowed to use the JS evaluation engine.
#[serde(default)]
pub allow_js: bool,
/// Whether the plugin's JS code can make HTTP requests via fetch polyfill.
#[serde(default)]
pub allow_js_fetch: bool,
pub display: DisplaySpec,
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct ProvidesSpec {
#[serde(default)] pub home: bool,
#[serde(default)] pub category: bool,
#[serde(default)] pub search: bool,
#[serde(default)] pub info: bool,
#[serde(default)] pub servers: bool,
#[serde(default)] pub stream: bool,
#[serde(default)] pub subtitles: bool,
#[serde(default)] pub articles: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct NetworkSpec {
#[serde(default = "default_hosts")] pub hosts: Vec<String>,
#[serde(default = "default_concurrent")] pub concurrent: usize,
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct DisplaySpec {
pub icon: Option<String>,
pub description: Option<String>,
#[serde(default)] pub tags: Vec<String>,
#[serde(default = "default_priority")] pub priority: u32,
#[serde(default)] pub adult: bool,
}
fn default_hosts() -> Vec<String> { vec!["*".into()] }
fn default_concurrent() -> usize { 8 }
fn default_priority() -> u32 { 100 }
impl Manifest {
pub fn validate(&self, host_version: &str) -> Result<(), BexError> {
if self.schema != 1 {
return Err(BexError::ManifestInvalid(format!("unsupported schema: {}", self.schema)));
}
if !self.id.contains('.') || self.id.len() < 5 {
return Err(BexError::ManifestInvalid(format!("id '{}' must be reverse-DNS", self.id)));
}
let req = semver::VersionReq::parse(&self.abi)
.map_err(|e| BexError::ManifestInvalid(format!("abi: {e}")))?;
let host = semver::Version::parse(host_version)
.map_err(|e| BexError::ManifestInvalid(format!("host: {e}")))?;
if !req.matches(&host) {
return Err(BexError::AbiMismatch { host: host_version.into(), plugin_requires: self.abi.clone() });
}
Ok(())
}
pub fn allows_host(&self, host: &str) -> bool {
self.network.hosts.iter().any(|p| {
if p == "*" { return true; }
if let Some(suffix) = p.strip_prefix("*.") {
host.ends_with(&format!(".{suffix}")) || host == suffix
} else { host == p }
})
}
pub fn capabilities(&self) -> Capabilities { Capabilities::from_manifest(&self.provides) }
}