mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-04-19 19:14:53 +00:00
189 lines
6.1 KiB
Rust
189 lines
6.1 KiB
Rust
pub mod add_custom_plugin_win;
|
|
pub mod store;
|
|
mod store_detail;
|
|
mod store_row_factory;
|
|
|
|
use crate::{
|
|
constants::APP_ID,
|
|
downloader::{cache_file_path, download_file_async},
|
|
file_builders::wayvr_dashboard_config::{
|
|
WayVrDashboardConfigFragment, WayVrDashboardConfigFragmentInner,
|
|
},
|
|
paths::get_plugins_dir,
|
|
util::file_utils::{get_writer, mark_as_executable},
|
|
xdg::XDG,
|
|
};
|
|
use anyhow::bail;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::{
|
|
collections::HashMap,
|
|
fs::{create_dir_all, remove_file},
|
|
path::PathBuf,
|
|
};
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)]
|
|
pub enum PluginType {
|
|
#[default]
|
|
Executable,
|
|
WayVrApp,
|
|
WayVrDashboard,
|
|
}
|
|
|
|
impl PluginType {
|
|
pub fn launches_directly(&self) -> bool {
|
|
self == &Self::Executable
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)]
|
|
pub struct Plugin {
|
|
pub appid: String,
|
|
pub name: String,
|
|
pub author: Option<String>,
|
|
pub icon_url: Option<String>,
|
|
pub version: Option<String>,
|
|
pub short_description: Option<String>,
|
|
pub description: Option<String>,
|
|
pub homepage_url: Option<String>,
|
|
pub screenshots: Vec<String>,
|
|
/// either one of exec_url or exec_path must be provided
|
|
pub exec_url: Option<String>,
|
|
/// either one of exec_url or exec_path must be provided
|
|
pub exec_path: Option<PathBuf>,
|
|
/// options and arguments that should be passed to the plugin executable
|
|
pub args: Option<Vec<String>>,
|
|
pub env_vars: Option<HashMap<String, String>>,
|
|
/// defined as a list of appids of other plugins
|
|
pub dependencies: Option<Vec<String>>,
|
|
/// defined as a list of appids of other plugins
|
|
/// all plugins of type WayVrDashboard should conflict with each other by default
|
|
pub conflicts: Option<Vec<String>>,
|
|
#[serde(default = "PluginType::default")]
|
|
pub plugin_type: PluginType,
|
|
}
|
|
|
|
impl Plugin {
|
|
fn wayvr_config_fragment_filename(&self) -> String {
|
|
format!("99-{APP_ID}-plugin.{}.yaml", self.appid)
|
|
}
|
|
|
|
fn wayvr_conf_dir() -> PathBuf {
|
|
XDG.get_config_home().join("wlxoverlay/wayvr.conf.d")
|
|
}
|
|
|
|
pub fn enable(&self) -> anyhow::Result<()> {
|
|
match self.plugin_type {
|
|
PluginType::Executable => {}
|
|
PluginType::WayVrApp => todo!(),
|
|
PluginType::WayVrDashboard => {
|
|
let wayvr_conf_dir = Self::wayvr_conf_dir();
|
|
if !wayvr_conf_dir.exists() {
|
|
create_dir_all(&wayvr_conf_dir)?;
|
|
} else if wayvr_conf_dir.is_file() {
|
|
bail!("wayvr.conf.d is a file and not a directory")
|
|
}
|
|
let config_fragment = WayVrDashboardConfigFragment {
|
|
dashboard: WayVrDashboardConfigFragmentInner {
|
|
exec: self
|
|
.executable()
|
|
.ok_or(anyhow::Error::msg("executable missing"))?
|
|
.to_string_lossy()
|
|
.to_string(),
|
|
args: self.args.as_ref().map(|args| args.join(" ")),
|
|
env: self
|
|
.env_vars
|
|
.as_ref()
|
|
.map(|vars| vars.iter().map(|(k, v)| format!("{k}={v}")).collect()),
|
|
},
|
|
};
|
|
let writer =
|
|
get_writer(&wayvr_conf_dir.join(self.wayvr_config_fragment_filename()))?;
|
|
serde_yaml::to_writer(writer, &config_fragment)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn disable(&self) -> anyhow::Result<()> {
|
|
match self.plugin_type {
|
|
PluginType::Executable => {}
|
|
PluginType::WayVrApp => todo!(),
|
|
PluginType::WayVrDashboard => {
|
|
let wayvr_conf_dir = Self::wayvr_conf_dir();
|
|
remove_file(wayvr_conf_dir.join(self.wayvr_config_fragment_filename()))?;
|
|
}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
pub fn set_enabled(&self, enabled: bool) -> anyhow::Result<()> {
|
|
if enabled {
|
|
self.enable()
|
|
} else {
|
|
self.disable()
|
|
}
|
|
}
|
|
|
|
pub fn executable(&self) -> Option<PathBuf> {
|
|
if self.exec_path.is_some() {
|
|
self.exec_path.clone()
|
|
} else {
|
|
let canonical = self.canonical_exec_path();
|
|
if canonical.is_file() {
|
|
Some(canonical)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn canonical_exec_path(&self) -> PathBuf {
|
|
get_plugins_dir().join(&self.appid)
|
|
}
|
|
|
|
pub fn is_installed(&self) -> bool {
|
|
self.executable().as_ref().is_some_and(|p| p.is_file())
|
|
}
|
|
|
|
pub fn mark_as_executable(&self) -> anyhow::Result<()> {
|
|
if let Some(p) = self.executable().as_ref() {
|
|
mark_as_executable(p)
|
|
} else {
|
|
bail!("no executable found for plugin")
|
|
}
|
|
}
|
|
|
|
/// validate if the plugin can be displayed correctly and run
|
|
pub fn validate(&self) -> bool {
|
|
!self.appid.is_empty()
|
|
&& !self.name.is_empty()
|
|
&& self.executable().as_ref().is_some_and(|p| p.is_file())
|
|
}
|
|
}
|
|
|
|
/// urls to manifest json files representing plugins.
|
|
/// each manifest should be json and the link should always point to the latest version
|
|
const MANIFESTS: [&str;2] = [
|
|
"https://github.com/galister/wlx-overlay-s/raw/refs/heads/meta/com.github.galister.wlx-overlay-s.json",
|
|
"https://github.com/StardustXR/telescope/raw/refs/heads/main/envision/org.stardustxr.telescope.json",
|
|
// wayvr dashboard potentially unsafe
|
|
];
|
|
|
|
pub async fn refresh_plugins() -> anyhow::Result<Vec<anyhow::Result<Plugin>>> {
|
|
let mut results = Vec::new();
|
|
for jh in MANIFESTS
|
|
.iter()
|
|
.map(|url| -> tokio::task::JoinHandle<anyhow::Result<Plugin>> {
|
|
tokio::spawn(async move {
|
|
let path = cache_file_path(url, Some("json"));
|
|
download_file_async(url, &path).await?;
|
|
Ok(serde_json::from_str::<Plugin>(
|
|
&tokio::fs::read_to_string(path).await?,
|
|
)?)
|
|
})
|
|
})
|
|
{
|
|
results.push(jh.await?);
|
|
}
|
|
Ok(results)
|
|
}
|