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
This commit is contained in:
parent
6bec2c0b30
commit
6e6e2691a8
7 changed files with 127 additions and 27 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -4095,6 +4095,7 @@ dependencies = [
|
||||||
"miette 3.3.0",
|
"miette 3.3.0",
|
||||||
"nix 0.23.1",
|
"nix 0.23.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"percent-encoding",
|
||||||
"regex",
|
"regex",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ lazy_static = "1.4.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
nix = "0.23.1"
|
nix = "0.23.1"
|
||||||
once_cell = "1.8.0"
|
once_cell = "1.8.0"
|
||||||
|
percent-encoding = "2.1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
strip-ansi-escapes = "0.1.0"
|
strip-ansi-escapes = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ use std::str::FromStr;
|
||||||
use super::plugins::{PluginTag, PluginsConfigError};
|
use super::plugins::{PluginTag, PluginsConfigError};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
|
|
@ -221,6 +220,35 @@ pub enum RunPluginLocation {
|
||||||
Zellij(PluginTag),
|
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 {
|
impl From<&RunPluginLocation> for Url {
|
||||||
fn from(location: &RunPluginLocation) -> Self {
|
fn from(location: &RunPluginLocation) -> Self {
|
||||||
let url = match location {
|
let url = match location {
|
||||||
|
|
@ -965,21 +993,6 @@ fn split_space(
|
||||||
Ok(pane_positions)
|
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 {
|
impl Default for SplitDirection {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SplitDirection::Horizontal
|
SplitDirection::Horizontal
|
||||||
|
|
|
||||||
|
|
@ -225,8 +225,10 @@ impl Default for PluginType {
|
||||||
pub enum PluginsConfigError {
|
pub enum PluginsConfigError {
|
||||||
#[error("Duplication in plugin tag names is not allowed: '{}'", String::from(.0.clone()))]
|
#[error("Duplication in plugin tag names is not allowed: '{}'", String::from(.0.clone()))]
|
||||||
DuplicatePlugins(PluginTag),
|
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.")]
|
#[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:?}'")]
|
#[error("Could not find plugin at the path: '{0:?}'")]
|
||||||
InvalidPluginLocation(PathBuf),
|
InvalidPluginLocation(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None);
|
||||||
assert!(layout.is_err(), "error provided for tab name with space");
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,8 @@ use crate::{
|
||||||
kdl_string_arguments,
|
kdl_string_arguments,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum PaneOrFloatingPane {
|
pub enum PaneOrFloatingPane {
|
||||||
|
|
@ -297,14 +295,13 @@ impl<'a> KdlLayoutParser<'a> {
|
||||||
plugin_block.span().len(),
|
plugin_block.span().len(),
|
||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
let url = Url::parse(string_url).map_err(|e| {
|
let location = RunPluginLocation::parse(&string_url).map_err(|e| {
|
||||||
ConfigError::new_layout_kdl_error(
|
ConfigError::new_layout_kdl_error(
|
||||||
format!("Failed to parse url: {:?}", e),
|
e.to_string(),
|
||||||
url_node.span().offset(),
|
url_node.span().offset(),
|
||||||
url_node.span().len(),
|
url_node.span().len(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let location = RunPluginLocation::try_from(url)?;
|
|
||||||
Ok(Some(Run::Plugin(RunPlugin {
|
Ok(Some(Run::Plugin(RunPlugin {
|
||||||
_allow_exec_host_cmd,
|
_allow_exec_host_cmd,
|
||||||
location,
|
location,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use miette::NamedSource;
|
use miette::NamedSource;
|
||||||
|
|
||||||
|
|
@ -1387,14 +1386,13 @@ impl RunPlugin {
|
||||||
kdl_node.span().len(),
|
kdl_node.span().len(),
|
||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
let url = Url::parse(string_url).map_err(|e| {
|
let location = RunPluginLocation::parse(string_url).map_err(|e| {
|
||||||
ConfigError::new_kdl_error(
|
ConfigError::new_layout_kdl_error(
|
||||||
format!("Failed to parse url: {:?}", e),
|
e.to_string(),
|
||||||
kdl_node.span().offset(),
|
kdl_node.span().offset(),
|
||||||
kdl_node.span().len(),
|
kdl_node.span().len(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let location = RunPluginLocation::try_from(url)?;
|
|
||||||
Ok(RunPlugin {
|
Ok(RunPlugin {
|
||||||
_allow_exec_host_cmd,
|
_allow_exec_host_cmd,
|
||||||
location,
|
location,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue