summaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/input
diff options
context:
space:
mode:
authorCory Forsstrom <cforsstrom18@gmail.com>2023-03-01 07:11:16 -0800
committerGitHub <noreply@github.com>2023-03-01 16:11:16 +0100
commit6e6e2691a8b8435ac7a8535cf0c4e0645ec20fa2 (patch)
tree56bf1dec9ea1eb4dfd89b07ee70b2e2f5435522b /zellij-utils/src/input
parent6bec2c0b30cff3bade926f379a167d51cdd4a5cc (diff)
fix(plugins): location path decoding from Url (#2190)
* Add unit test for plugin run location parsing * Fix file plugin parsing for relative paths * Update test to check for path with spaces * Add a couple more tests
Diffstat (limited to 'zellij-utils/src/input')
-rw-r--r--zellij-utils/src/input/layout.rs45
-rw-r--r--zellij-utils/src/input/plugins.rs4
-rw-r--r--zellij-utils/src/input/unit/layout_test.rs88
3 files changed, 120 insertions, 17 deletions
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index 915c348a8..d06833416 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -23,7 +23,6 @@ use std::str::FromStr;
use super::plugins::{PluginTag, PluginsConfigError};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
-use std::convert::TryFrom;
use std::vec::Vec;
use std::{
fmt,
@@ -221,6 +220,35 @@ pub enum RunPluginLocation {
Zellij(PluginTag),
}
+impl RunPluginLocation {
+ pub fn parse(location: &str) -> Result<Self, PluginsConfigError> {
+ let url = Url::parse(location)?;
+
+ let decoded_path = percent_encoding::percent_decode_str(url.path()).decode_utf8_lossy();
+
+ match url.scheme() {
+ "zellij" => Ok(Self::Zellij(PluginTag::new(decoded_path))),
+ "file" => {
+ let path = if location.starts_with("file:/") {
+ // Path is absolute, its safe to use URL path.
+ //
+ // This is the case if the scheme and : delimiter are followed by a / slash
+ decoded_path
+ } else {
+ // URL dep doesn't handle relative paths with `file` schema properly,
+ // it always makes them absolute. Use raw location string instead.
+ //
+ // Unwrap is safe here since location is a valid URL
+ location.strip_prefix("file:").unwrap().into()
+ };
+
+ Ok(Self::File(PathBuf::from(path.as_ref())))
+ },
+ _ => Err(PluginsConfigError::InvalidUrlScheme(url)),
+ }
+ }
+}
+
impl From<&RunPluginLocation> for Url {
fn from(location: &RunPluginLocation) -> Self {
let url = match location {
@@ -965,21 +993,6 @@ fn split_space(
Ok(pane_positions)
}
-impl TryFrom<Url> for RunPluginLocation {
- type Error = PluginsConfigError;
-
- fn try_from(url: Url) -> Result<Self, Self::Error> {
- match url.scheme() {
- "zellij" => Ok(Self::Zellij(PluginTag::new(url.path()))),
- "file" => {
- let path = PathBuf::from(url.path());
- Ok(Self::File(path))
- },
- _ => Err(PluginsConfigError::InvalidUrl(url)),
- }
- }
-}
-
impl Default for SplitDirection {
fn default() -> Self {
SplitDirection::Horizontal
diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs
index 1ab3e831f..47cc3aa74 100644
--- a/zellij-utils/src/input/plugins.rs
+++ b/zellij-utils/src/input/plugins.rs
@@ -225,8 +225,10 @@ impl Default for PluginType {
pub enum PluginsConfigError {
#[error("Duplication in plugin tag names is not allowed: '{}'", String::from(.0.clone()))]
DuplicatePlugins(PluginTag),
+ #[error("Failed to parse url: {0:?}")]
+ InvalidUrl(#[from] url::ParseError),
#[error("Only 'file:' and 'zellij:' url schemes are supported for plugin lookup. '{0}' does not match either.")]
- InvalidUrl(Url),
+ InvalidUrlScheme(Url),
#[error("Could not find plugin at the path: '{0:?}'")]
InvalidPluginLocation(PathBuf),
}
diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs
index 670273b0f..c8c6c64dc 100644
--- a/zellij-utils/src/input/unit/layout_test.rs
+++ b/zellij-utils/src/input/unit/layout_test.rs
@@ -1942,3 +1942,91 @@ fn cannot_define_stacked_panes_with_grandchildren_in_pane_template() {
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None);
assert!(layout.is_err(), "error provided for tab name with space");
}
+
+fn run_plugin_location_parsing() {
+ let kdl_layout = r#"
+ layout {
+ pane {
+ plugin location="zellij:tab-bar"
+ }
+ pane {
+ plugin location="file:/path/to/my/plugin.wasm"
+ }
+ pane {
+ plugin location="file:plugin.wasm"
+ }
+ pane {
+ plugin location="file:relative/with space/plugin.wasm"
+ }
+ pane {
+ plugin location="file:///absolute/with space/plugin.wasm"
+ }
+ pane {
+ plugin location="file:c:/absolute/windows/plugin.wasm"
+ }
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap();
+ let expected_layout = Layout {
+ template: Some((
+ TiledPaneLayout {
+ children: vec![
+ TiledPaneLayout {
+ run: Some(Run::Plugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")),
+ })),
+ ..Default::default()
+ },
+ TiledPaneLayout {
+ run: Some(Run::Plugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(
+ "/path/to/my/plugin.wasm",
+ )),
+ })),
+ ..Default::default()
+ },
+ TiledPaneLayout {
+ run: Some(Run::Plugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from("plugin.wasm")),
+ })),
+ ..Default::default()
+ },
+ TiledPaneLayout {
+ run: Some(Run::Plugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(
+ "relative/with space/plugin.wasm",
+ )),
+ })),
+ ..Default::default()
+ },
+ TiledPaneLayout {
+ run: Some(Run::Plugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(
+ "/absolute/with space/plugin.wasm",
+ )),
+ })),
+ ..Default::default()
+ },
+ TiledPaneLayout {
+ run: Some(Run::Plugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(
+ "c:/absolute/windows/plugin.wasm",
+ )),
+ })),
+ ..Default::default()
+ },
+ ],
+ ..Default::default()
+ },
+ vec![],
+ )),
+ ..Default::default()
+ };
+ assert_eq!(layout, expected_layout);
+}