use super::{
workspace_err::ConfigVariantError, workspace_err::NoConfigVariantError,
workspace_err::WorkspaceError,
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, collections::HashSet, path::Path, path::PathBuf, sync::Arc};
use tab_api::tab::normalize_name;
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct WorkspaceState {
pub tabs: Arc<Vec<WorkspaceTab>>,
pub errors: Vec<String>,
}
impl WorkspaceState {
pub fn into_name_set(&self) -> HashSet<String> {
self.tabs.iter().map(|tab| tab.name.clone()).collect()
}
}
#[derive(TypedBuilder, Debug, Clone, Eq, PartialEq)]
pub struct WorkspaceTab {
pub name: String,
#[builder(default, setter(strip_option))]
pub doc: Option<String>,
pub directory: PathBuf,
#[builder(default, setter(strip_option))]
pub shell: Option<String>,
#[builder(default, setter(strip_option))]
pub env: Option<HashMap<String, String>>, }
impl WorkspaceTab {
pub fn new(name: &str, directory: PathBuf) -> Self {
let name = normalize_name(name);
Self {
name,
directory,
shell: None,
doc: None,
env: None,
}
}
pub fn with_options(name: &str, directory: PathBuf, options: TabOptions) -> Self {
let name = normalize_name(name);
Self {
name,
directory,
shell: options.shell,
doc: options.doc,
env: options.env,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Config {
#[serde(rename = "workspace")]
Workspace(Workspace),
#[serde(rename = "repo")]
Repo(Repo),
None,
}
impl Config {
pub fn as_workspace(self, dir: &Path) -> Result<Workspace, WorkspaceError> {
if let Config::Workspace(workspace) = self {
return Ok(workspace);
}
if let Config::None = self {
return Err(WorkspaceError::NoConfigVariant(NoConfigVariantError {
path: dir.to_path_buf(),
expected: "Workspace",
}));
}
Err(WorkspaceError::ConfigVariantError(ConfigVariantError {
path: dir.to_path_buf(),
expected: "Workspace",
found: self.variant_name(),
}))
}
pub fn as_repo(self, dir: &Path) -> Result<Repo, WorkspaceError> {
if let Config::Repo(repo) = self {
return Ok(repo);
}
if let Config::None = self {
return Err(WorkspaceError::NoConfigVariant(NoConfigVariantError {
path: dir.to_path_buf(),
expected: "Repo",
}));
}
Err(WorkspaceError::ConfigVariantError(ConfigVariantError {
path: dir.to_path_buf(),
expected: "Repo",
found: self.variant_name(),
}))
}
fn variant_name(&self) -> &'static str {
match self {
Config::Workspace(_) => "Workspace",
Config::Repo(_) => "Workspace",
Config::None => "None",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Workspace {
pub tab: Option<String>,
#[serde(flatten)]
pub options: TabOptions,
pub workspace: Vec<WorkspaceItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum WorkspaceItem {
Workspace(WorkspaceLink),
Repo(WorkspaceRepoLink),
Tab(Tab),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceLink {
pub workspace: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceRepoLink {
pub repo: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Repo {
pub repo: String,
#[serde(flatten)]
pub tab_options: TabOptions,
pub tabs: Option<Vec<Tab>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tab {
pub tab: String,
pub dir: Option<String>,
#[serde(flatten)]
pub options: TabOptions,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TabOptions {
pub doc: Option<String>,
pub shell: Option<String>,
pub env: Option<HashMap<String, String>>,
}
impl Default for TabOptions {
fn default() -> Self {
Self {
doc: None,
shell: None,
env: None,
}
}
}
impl TabOptions {
pub fn or(self, other: Self) -> Self {
let env = if let Some(mut env) = self.env {
if let Some(other_env) = other.env {
for (key, value) in other_env.into_iter() {
if !env.contains_key(&key) {
env.insert(key, value);
}
}
}
Some(env)
} else {
other.env
};
Self {
doc: self.doc,
shell: self.shell.or(other.shell),
env,
}
}
}