use serde::{Deserialize, Serialize};
use tatara_lisp::DeriveTataraDomain;
#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[tatara(keyword = "defsession")]
pub struct SessionSpec {
pub name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub buffers: Vec<String>,
#[serde(default)]
pub layout: String,
#[serde(default)]
pub cwd: String,
#[serde(default)]
pub keybind: String,
#[serde(default)]
pub on_enter: Vec<String>,
#[serde(default)]
pub on_leave: Vec<String>,
}
pub const KNOWN_LAYOUTS: &[&str] = &[
"single", "horizontal", "vertical", "grid-2x2", "grid-3x3", "main-side", "tabs", ];
#[must_use]
pub fn is_known_layout(name: &str) -> bool {
name.is_empty() || KNOWN_LAYOUTS.iter().any(|l| *l == name)
}
impl SessionSpec {
#[must_use]
pub fn has_buffers(&self) -> bool {
!self.buffers.is_empty()
}
#[must_use]
pub fn pane_count(&self) -> usize {
match self.layout.as_str() {
"single" | "tabs" => 1,
"grid-2x2" => 4,
"grid-3x3" => 9,
_ => self.buffers.len().max(1),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_layout_accepts_empty_and_canonicals() {
assert!(is_known_layout(""));
assert!(is_known_layout("single"));
assert!(is_known_layout("horizontal"));
assert!(is_known_layout("grid-2x2"));
assert!(!is_known_layout("zigzag"));
}
#[test]
fn pane_count_handles_canonical_layouts() {
let mut s = SessionSpec {
name: "x".into(),
layout: "single".into(),
buffers: vec!["a".into(), "b".into(), "c".into()],
..Default::default()
};
assert_eq!(s.pane_count(), 1);
s.layout = "horizontal".into();
assert_eq!(s.pane_count(), 3);
s.layout = "grid-2x2".into();
assert_eq!(s.pane_count(), 4);
s.layout = "grid-3x3".into();
assert_eq!(s.pane_count(), 9);
s.layout = "tabs".into();
assert_eq!(s.pane_count(), 1);
s.layout = "custom-plugin-layout".into();
assert_eq!(s.pane_count(), 3);
}
#[test]
fn has_buffers_checks_emptiness() {
let a = SessionSpec {
name: "x".into(),
buffers: vec!["a".into()],
..Default::default()
};
let b = SessionSpec {
name: "y".into(),
..Default::default()
};
assert!(a.has_buffers());
assert!(!b.has_buffers());
}
}
impl Default for SessionSpec {
fn default() -> Self {
Self {
name: String::new(),
description: String::new(),
buffers: Vec::new(),
layout: String::new(),
cwd: String::new(),
keybind: String::new(),
on_enter: Vec::new(),
on_leave: Vec::new(),
}
}
}