summaryrefslogtreecommitdiffstats
path: root/zellij-client/src/old_config_converter/old_layout.rs
diff options
context:
space:
mode:
Diffstat (limited to 'zellij-client/src/old_config_converter/old_layout.rs')
-rw-r--r--zellij-client/src/old_config_converter/old_layout.rs557
1 files changed, 557 insertions, 0 deletions
diff --git a/zellij-client/src/old_config_converter/old_layout.rs b/zellij-client/src/old_config_converter/old_layout.rs
new file mode 100644
index 000000000..cd1aa66fb
--- /dev/null
+++ b/zellij-client/src/old_config_converter/old_layout.rs
@@ -0,0 +1,557 @@
+// This is a converter from the old yaml layout to the new KDL layout.
+//
+// It is supposed to be mostly self containing - please refrain from adding to it, importing
+// from it or changing it
+use super::old_config::{config_yaml_to_config_kdl, OldConfigFromYaml, OldRunCommand};
+use serde::{Deserialize, Serialize};
+use std::vec::Vec;
+use std::{fmt, path::PathBuf};
+use url::Url;
+
+fn pane_line(
+ pane_name: Option<&String>,
+ split_size: Option<OldSplitSize>,
+ focus: Option<bool>,
+ borderless: bool,
+) -> String {
+ let mut pane_line = format!("pane");
+ if let Some(pane_name) = pane_name {
+ pane_line.push_str(&format!(" name=\"{}\"", pane_name));
+ }
+ if let Some(split_size) = split_size {
+ pane_line.push_str(&format!(" size={}", split_size));
+ }
+ if let Some(focus) = focus {
+ pane_line.push_str(&format!(" focus={}", focus));
+ }
+ if borderless {
+ pane_line.push_str(" borderless=true");
+ }
+ pane_line
+}
+
+fn tab_line(
+ pane_name: Option<&String>,
+ split_size: Option<OldSplitSize>,
+ focus: Option<bool>,
+ borderless: bool,
+) -> String {
+ let mut pane_line = format!("tab");
+ if let Some(pane_name) = pane_name {
+ pane_line.push_str(&format!(" name=\"{}\"", pane_name));
+ }
+ if let Some(split_size) = split_size {
+ pane_line.push_str(&format!(" size={}", split_size));
+ }
+ if let Some(focus) = focus {
+ pane_line.push_str(&format!(" focus={}", focus));
+ }
+ if borderless {
+ pane_line.push_str(" borderless=true");
+ }
+ pane_line
+}
+
+fn pane_line_with_children(
+ pane_name: Option<&String>,
+ split_size: Option<OldSplitSize>,
+ focus: Option<bool>,
+ borderless: bool,
+ split_direction: OldDirection,
+) -> String {
+ let mut pane_line = format!("pane");
+ if let Some(pane_name) = pane_name {
+ pane_line.push_str(&format!(" name=\"{}\"", pane_name));
+ }
+ if let Some(split_size) = split_size {
+ pane_line.push_str(&format!(" size={}", split_size));
+ }
+ if let Some(focus) = focus {
+ pane_line.push_str(&format!(" focus={}", focus));
+ }
+ pane_line.push_str(&format!(" split_direction=\"{}\"", split_direction));
+ if borderless {
+ pane_line.push_str(" borderless=true");
+ }
+ pane_line
+}
+
+fn pane_command_line(
+ pane_name: Option<&String>,
+ split_size: Option<OldSplitSize>,
+ focus: Option<bool>,
+ borderless: bool,
+ command: &PathBuf,
+) -> String {
+ let mut pane_line = format!("pane command={:?}", command);
+ if let Some(pane_name) = pane_name {
+ pane_line.push_str(&format!(" name=\"{}\"", pane_name));
+ }
+ if let Some(split_size) = split_size {
+ pane_line.push_str(&format!(" size={}", split_size));
+ }
+ if let Some(focus) = focus {
+ pane_line.push_str(&format!(" focus={}", focus));
+ }
+ if borderless {
+ pane_line.push_str(" borderless=true");
+ }
+ pane_line
+}
+
+fn tab_line_with_children(
+ pane_name: Option<&String>,
+ split_size: Option<OldSplitSize>,
+ focus: Option<bool>,
+ borderless: bool,
+ split_direction: OldDirection,
+) -> String {
+ let mut pane_line = format!("tab");
+ if let Some(pane_name) = pane_name {
+ pane_line.push_str(&format!(" name=\"{}\"", pane_name));
+ }
+ if let Some(split_size) = split_size {
+ pane_line.push_str(&format!(" size={}", split_size));
+ }
+ if let Some(focus) = focus {
+ pane_line.push_str(&format!(" focus={}", focus));
+ }
+ pane_line.push_str(&format!(" split_direction=\"{}\"", split_direction));
+ if borderless {
+ pane_line.push_str(" borderless=true");
+ }
+ pane_line
+}
+
+fn stringify_template(
+ template: &OldLayoutTemplate,
+ indentation: String,
+ has_no_tabs: bool,
+ is_base: bool,
+) -> String {
+ let mut stringified = if is_base {
+ String::new()
+ } else {
+ String::from("\n")
+ };
+ if is_base && !template.parts.is_empty() && template.direction == OldDirection::Vertical {
+ // we don't support specifying the split direction in the layout node
+ // eg. layout split_direction="Vertical" { .. } <== this is not supported!!
+ // so we need to add a child wrapper with the split direction instead:
+ // layout {
+ // pane split_direction="Vertical" { .. }
+ // }
+ let child_indentation = format!("{} ", &indentation);
+ stringified.push_str(&stringify_template(
+ template,
+ child_indentation,
+ has_no_tabs,
+ false,
+ ));
+ } else if !template.parts.is_empty() {
+ if !is_base {
+ stringified.push_str(&format!(
+ "{}{} {{",
+ indentation,
+ pane_line_with_children(
+ template.pane_name.as_ref(),
+ template.split_size,
+ template.focus,
+ template.borderless,
+ template.direction
+ )
+ ));
+ }
+ for part in &template.parts {
+ let child_indentation = format!("{} ", &indentation);
+ stringified.push_str(&stringify_template(
+ &part,
+ child_indentation,
+ has_no_tabs,
+ false,
+ ));
+ }
+ if !is_base {
+ stringified.push_str(&format!("\n{}}}", indentation));
+ }
+ } else if template.body && !has_no_tabs {
+ stringified.push_str(&format!("{}children", indentation));
+ } else {
+ match template.run.as_ref() {
+ Some(OldRunFromYaml::Plugin(plugin_from_yaml)) => {
+ stringified.push_str(&format!(
+ "{}{} {{\n",
+ &indentation,
+ pane_line(
+ template.pane_name.as_ref(),
+ template.split_size,
+ template.focus,
+ template.borderless
+ )
+ ));
+ stringified.push_str(&format!(
+ "{} plugin location=\"{}\"\n",
+ &indentation, plugin_from_yaml.location
+ ));
+ stringified.push_str(&format!("{}}}", &indentation));
+ },
+ Some(OldRunFromYaml::Command(command_from_yaml)) => {
+ stringified.push_str(&format!(
+ "{}{}",
+ &indentation,
+ &pane_command_line(
+ template.pane_name.as_ref(),
+ template.split_size,
+ template.focus,
+ template.borderless,
+ &command_from_yaml.command
+ )
+ ));
+ if let Some(cwd) = command_from_yaml.cwd.as_ref() {
+ stringified.push_str(&format!(" cwd={:?}", cwd));
+ }
+ if !command_from_yaml.args.is_empty() {
+ stringified.push_str(" {\n");
+ stringified.push_str(&format!(
+ "{} args {}\n",
+ &indentation,
+ command_from_yaml
+ .args
+ .iter()
+ .map(|s| format!("\"{}\"", s))
+ .collect::<Vec<String>>()
+ .join(" ")
+ ));
+ stringified.push_str(&format!("{}}}", &indentation));
+ }
+ },
+ None => {
+ stringified.push_str(&format!(
+ "{}{}",
+ &indentation,
+ pane_line(
+ template.pane_name.as_ref(),
+ template.split_size,
+ template.focus,
+ template.borderless
+ )
+ ));
+ },
+ };
+ }
+ stringified
+}
+
+fn stringify_tabs(tabs: Vec<OldTabLayout>) -> String {
+ let mut stringified = String::new();
+ for tab in tabs {
+ let child_indentation = String::from(" ");
+ if !tab.parts.is_empty() {
+ stringified.push_str(&format!(
+ "\n{}{} {{",
+ child_indentation,
+ tab_line_with_children(
+ tab.pane_name.as_ref(),
+ tab.split_size,
+ tab.focus,
+ tab.borderless,
+ tab.direction
+ )
+ ));
+ let tab_template = OldLayoutTemplate::from(tab);
+ stringified.push_str(&stringify_template(
+ &tab_template,
+ child_indentation.clone(),
+ true,
+ true,
+ ));
+ stringified.push_str(&format!("\n{}}}", child_indentation));
+ } else {
+ stringified.push_str(&format!(
+ "\n{}{}",
+ child_indentation,
+ tab_line(
+ tab.pane_name.as_ref(),
+ tab.split_size,
+ tab.focus,
+ tab.borderless
+ )
+ ));
+ }
+ }
+ stringified
+}
+
+pub fn layout_yaml_to_layout_kdl(raw_yaml_layout: &str) -> Result<String, String> {
+ // returns the raw kdl config
+ let layout_from_yaml: OldLayoutFromYamlIntermediate = serde_yaml::from_str(raw_yaml_layout)
+ .map_err(|e| format!("Failed to parse yaml: {:?}", e))?;
+ let mut kdl_layout = String::new();
+ kdl_layout.push_str("layout {");
+ let template = layout_from_yaml.template;
+ let tabs = layout_from_yaml.tabs;
+ let has_no_tabs = tabs.is_empty()
+ || tabs.len() == 1 && tabs.get(0).map(|t| t.parts.is_empty()).unwrap_or(false);
+ if has_no_tabs {
+ let indentation = String::from("");
+ kdl_layout.push_str(&stringify_template(
+ &template,
+ indentation,
+ has_no_tabs,
+ true,
+ ));
+ } else {
+ kdl_layout.push_str("\n default_tab_template {");
+ let indentation = String::from(" ");
+ kdl_layout.push_str(&stringify_template(
+ &template,
+ indentation,
+ has_no_tabs,
+ true,
+ ));
+ kdl_layout.push_str("\n }");
+ kdl_layout.push_str(&stringify_tabs(tabs));
+ }
+ kdl_layout.push_str("\n}");
+ let layout_config = config_yaml_to_config_kdl(raw_yaml_layout, true)?;
+ if let Some(session_name) = layout_from_yaml.session.name {
+ kdl_layout.push_str(&format!("\nsession_name \"{}\"", session_name));
+ if let Some(attach_to_session) = layout_from_yaml.session.attach {
+ kdl_layout.push_str(&format!("\nattach_to_session {}", attach_to_session));
+ }
+ }
+ if !layout_config.is_empty() {
+ kdl_layout.push('\n');
+ }
+ kdl_layout.push_str(&layout_config);
+ Ok(kdl_layout)
+}
+
+#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
+pub enum OldDirection {
+ #[serde(alias = "horizontal")]
+ Horizontal,
+ #[serde(alias = "vertical")]
+ Vertical,
+}
+
+impl fmt::Display for OldDirection {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self {
+ Self::Horizontal => write!(f, "Horizontal"),
+ Self::Vertical => write!(f, "Vertical"),
+ }
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
+pub enum OldSplitSize {
+ #[serde(alias = "percent")]
+ Percent(u64), // 1 to 100
+ #[serde(alias = "fixed")]
+ Fixed(usize), // An absolute number of columns or rows
+}
+
+impl fmt::Display for OldSplitSize {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self {
+ Self::Percent(percent) => write!(f, "\"{}%\"", percent),
+ Self::Fixed(fixed_size) => write!(f, "{}", fixed_size),
+ }
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+pub enum OldRunFromYaml {
+ #[serde(rename = "plugin")]
+ Plugin(OldRunPluginFromYaml),
+ #[serde(rename = "command")]
+ Command(OldRunCommand),
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+pub struct OldRunPluginFromYaml {
+ #[serde(default)]
+ pub _allow_exec_host_cmd: bool,
+ pub location: Url,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+#[serde(default)]
+pub struct OldLayoutFromYamlIntermediate {
+ #[serde(default)]
+ pub template: OldLayoutTemplate,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub tabs: Vec<OldTabLayout>,
+ #[serde(default)]
+ pub session: OldSessionFromYaml,
+ #[serde(flatten)]
+ pub config: Option<OldConfigFromYaml>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
+#[serde(default)]
+pub struct OldLayoutFromYaml {
+ #[serde(default)]
+ pub session: OldSessionFromYaml,
+ #[serde(default)]
+ pub template: OldLayoutTemplate,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub tabs: Vec<OldTabLayout>,
+}
+
+#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
+pub struct OldSessionFromYaml {
+ pub name: Option<String>,
+ #[serde(default = "default_as_some_true")]
+ pub attach: Option<bool>,
+}
+
+fn default_as_some_true() -> Option<bool> {
+ Some(true)
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+pub struct OldLayoutTemplate {
+ pub direction: OldDirection,
+ #[serde(default)]
+ pub pane_name: Option<String>,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub parts: Vec<OldLayoutTemplate>,
+ #[serde(default)]
+ pub body: bool,
+ pub split_size: Option<OldSplitSize>,
+ pub focus: Option<bool>,
+ pub run: Option<OldRunFromYaml>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+pub struct OldTabLayout {
+ #[serde(default)]
+ pub direction: OldDirection,
+ pub pane_name: Option<String>,
+ #[serde(default)]
+ pub borderless: bool,
+ #[serde(default)]
+ pub parts: Vec<OldTabLayout>,
+ pub split_size: Option<OldSplitSize>,
+ #[serde(default)]
+ pub name: String,
+ pub focus: Option<bool>,
+ pub run: Option<OldRunFromYaml>,
+}
+
+impl From<OldTabLayout> for OldLayoutTemplate {
+ fn from(old_tab_layout: OldTabLayout) -> Self {
+ OldLayoutTemplate {
+ direction: old_tab_layout.direction,
+ pane_name: old_tab_layout.pane_name.clone(),
+ borderless: old_tab_layout.borderless,
+ parts: old_tab_layout.parts.iter().map(|o| o.into()).collect(),
+ split_size: old_tab_layout.split_size,
+ focus: old_tab_layout.focus,
+ run: old_tab_layout.run.clone(),
+ body: false,
+ }
+ }
+}
+
+impl From<&OldTabLayout> for OldLayoutTemplate {
+ fn from(old_tab_layout: &OldTabLayout) -> Self {
+ OldLayoutTemplate {
+ direction: old_tab_layout.direction,
+ pane_name: old_tab_layout.pane_name.clone(),
+ borderless: old_tab_layout.borderless,
+ parts: old_tab_layout.parts.iter().map(|o| o.into()).collect(),
+ split_size: old_tab_layout.split_size,
+ focus: old_tab_layout.focus,
+ run: old_tab_layout.run.clone(),
+ body: false,
+ }
+ }
+}
+
+impl From<&mut OldTabLayout> for OldLayoutTemplate {
+ fn from(old_tab_layout: &mut OldTabLayout) -> Self {
+ OldLayoutTemplate {
+ direction: old_tab_layout.direction,
+ pane_name: old_tab_layout.pane_name.clone(),
+ borderless: old_tab_layout.borderless,
+ parts: old_tab_layout.parts.iter().map(|o| o.into()).collect(),
+ split_size: old_tab_layout.split_size,
+ focus: old_tab_layout.focus,
+ run: old_tab_layout.run.clone(),
+ body: false,
+ }
+ }
+}
+
+impl From<OldLayoutFromYamlIntermediate> for OldLayoutFromYaml {
+ fn from(layout_from_yaml_intermediate: OldLayoutFromYamlIntermediate) -> Self {
+ Self {
+ template: layout_from_yaml_intermediate.template,
+ borderless: layout_from_yaml_intermediate.borderless,
+ tabs: layout_from_yaml_intermediate.tabs,
+ session: layout_from_yaml_intermediate.session,
+ }
+ }
+}
+
+impl From<OldLayoutFromYaml> for OldLayoutFromYamlIntermediate {
+ fn from(layout_from_yaml: OldLayoutFromYaml) -> Self {
+ Self {
+ template: layout_from_yaml.template,
+ borderless: layout_from_yaml.borderless,
+ tabs: layout_from_yaml.tabs,
+ config: None,
+ session: layout_from_yaml.session,
+ }
+ }
+}
+
+impl Default for OldLayoutFromYamlIntermediate {
+ fn default() -> Self {
+ OldLayoutFromYaml::default().into()
+ }
+}
+
+impl Default for OldLayoutTemplate {
+ fn default() -> Self {
+ Self {
+ direction: OldDirection::Horizontal,
+ pane_name: None,
+ body: false,
+ borderless: false,
+ parts: vec![OldLayoutTemplate {
+ direction: OldDirection::Horizontal,
+ pane_name: None,
+ body: true,
+ borderless: false,
+ split_size: None,
+ focus: None,
+ run: None,
+ parts: vec![],
+ }],
+ split_size: None,
+ focus: None,
+ run: None,
+ }
+ }
+}
+
+impl Default for OldDirection {
+ fn default() -> Self {
+ OldDirection::Horizontal
+ }
+}
+
+// The unit test location.
+#[path = "./unit/convert_layout_tests.rs"]
+#[cfg(test)]
+mod convert_layout_test;