Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imp: Support switching between sessions #50

Merged
merged 4 commits into from
Jan 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions load/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ version = "0.8.1-development"
authors = ["Brian Pearce"]
publish = false

[lib]
doctest = false

[dependencies]
common = { path = "../common" }
dirs = "2.0.2"
Expand Down
29 changes: 29 additions & 0 deletions load/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,27 @@ impl Command for SelectPane {
}
}

/// Used to switch to a daemonized session when already within a tmux session.
/// name: The named session to switch to.
#[derive(Debug, Clone)]
pub struct SwitchClient<'a> {
pub name: SessionTarget<'a>,
}

impl<'a> SwitchClient<'a> {
pub fn new(name: &'a str) -> SwitchClient<'a> {
SwitchClient {
name: SessionTarget::new(name),
}
}
}

impl<'a> Command for SwitchClient<'a> {
fn args(&self) -> Vec<&str> {
vec!["switch-client", "-t", &self.name.arg_string]
}
}

/// Used for executing the `pre` option to execute commands before building the
/// tmux session.
/// exec: The command to execute
Expand Down Expand Up @@ -295,6 +316,7 @@ pub enum Commands<'a> {
SendKeys(SendKeys),
Session(Session<'a>),
Split(Split),
SwitchClient(SwitchClient<'a>),
Window(Window<'a>),
}

Expand All @@ -309,6 +331,7 @@ impl<'a> Commands<'a> {
Commands::SendKeys(c) => c,
Commands::Session(c) => c,
Commands::Split(c) => c,
Commands::SwitchClient(c) => c,
Commands::Window(c) => c,
}
}
Expand Down Expand Up @@ -362,6 +385,12 @@ impl<'a> From<Split> for Commands<'a> {
}
}

impl<'a> From<SwitchClient<'a>> for Commands<'a> {
fn from(command: SwitchClient<'a>) -> Self {
Commands::SwitchClient(command)
}
}

impl<'a> From<Window<'a>> for Commands<'a> {
fn from(command: Window<'a>) -> Self {
Commands::Window(command)
Expand Down
83 changes: 76 additions & 7 deletions load/src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,51 @@
//! configs in.
pub mod parser;

use command::{Attach, Commands};
use command::{Attach, Commands, SwitchClient};
use common::project_paths::ProjectPaths;
use first_run::check_first_run;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use tmux::has_session;
use yaml_rust::{Yaml, YamlLoader};

static TMUX_ENV_VAR: &str = "TMUX";

/// Using the provided project name, locate the path to that project file. It
/// should be something similar to: `~/.muxed/my_project.yml`
/// Read in the contents of the config (which should be Yaml), and parse the
/// contents as yaml.
///
/// `project_name`: The name of the project, corresponding to the project config
/// file.
/// `project_paths`: The struct of paths
///
/// # Examples
///
/// Given the project name "compiler" and a project file found at:
/// `~/.muxed/compiler.yml`.
///
/// ```
/// let yaml: Result<Vec<Yaml>, String> = read("compiler".to_string());
/// ```
/// ```rust,no_run
/// extern crate common;
/// extern crate load;
/// extern crate yaml_rust;
///
/// `project_name`: The name of the project, corresponding to the project config
/// file.
/// use common::project_paths::ProjectPaths;
/// use load::project::read;
/// use std::path::PathBuf;
/// use yaml_rust::{Yaml, YamlLoader};
///
/// let paths = ProjectPaths::new(
/// PathBuf::from("/tmp"),
/// PathBuf::from("/tmp/.muxed"),
/// PathBuf::from("/tmp/.muxed/projectname.yml")
/// );
///
/// let yaml: Result<Vec<Yaml>, String> = read("compiler", &paths);
///
/// assert!(yaml.is_ok());
/// ```
pub fn read(project_name: &str, project_paths: &ProjectPaths) -> Result<Vec<Yaml>, String> {
check_first_run(&project_paths.project_directory)?;

Expand All @@ -46,12 +67,38 @@ pub fn read(project_name: &str, project_paths: &ProjectPaths) -> Result<Vec<Yaml
/// session is not active return None and let the app carry on.
pub fn session_exists(project_name: &str) -> Option<Commands> {
if has_session(project_name).success() {
Some(Attach::new(&project_name, None).into())
Some(open(project_name))
} else {
None
}
}

/// Check to see how we want to open the project. Do we need to attach to a new
/// tmux session or can we switch the client from a running session.
///
/// # Examples
///
/// ```rust
/// extern crate load;
///
/// use load::command::{Attach, Commands, Command};
/// use load::project::open;
///
/// let correct_type = match open("muxed") {
/// Commands::Attach(_) => true,
/// _ => false,
/// };
///
/// assert!(correct_type)
/// ```
pub fn open(project_name: &str) -> Commands {
if env::var_os(TMUX_ENV_VAR).is_some() {
SwitchClient::new(&project_name).into()
} else {
Attach::new(&project_name, None).into()
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -98,4 +145,26 @@ mod test {
let _ = fs::remove_file(&project_paths.project_file);
assert!(result.is_ok());
}

#[test]
fn open_returns_attach_in_bare_context() {
let attach_command = match open("muxed") {
Commands::Attach(_) => true,
_ => false,
};

assert!(attach_command);
}

#[test]
fn open_returns_switch_client_in_nested_context() {
let _ = env::set_var(TMUX_ENV_VAR, "somestring");
let switch_command = match open("muxed") {
Commands::SwitchClient(_) => true,
_ => false,
};
let _ = env::remove_var(TMUX_ENV_VAR);

assert!(switch_command);
}
}
3 changes: 2 additions & 1 deletion load/src/project/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use command::*;
use dirs::home_dir;
use project;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use tmux::config::Config;
Expand Down Expand Up @@ -172,7 +173,7 @@ pub fn call<'a>(
};

if !daemonize {
remains.push(Attach::new(&project_name, root).into());
remains.push(project::open(&project_name).into());
};

Ok(remains)
Expand Down
39 changes: 26 additions & 13 deletions load/src/tmux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ static TMUX_NAME: &str = "tmux";
///
/// # Examples
///
/// ```
/// let _ = call(&["new-window", "-t", "muxed", "-c", "~/Projects/muxed/"]);
/// ```rust
/// extern crate load;
/// use load::tmux::call;
///
/// let _ = call(&["new-window", "-t", "muxed-test", "-c", "~/Projects/muxed/"]);
/// let _ = call(&["kill-session", "-t", "muxed-test"]);
/// ```
pub fn call(args: &[&str]) -> Result<Output, io::Error> {
//println!("{:?}", &args);
Expand All @@ -35,14 +39,18 @@ pub fn call(args: &[&str]) -> Result<Output, io::Error> {

/// Has session is used firgure out if a named session is already running.
///
/// `target`: A string represented by the `{named_session}`
///
/// # Examples
///
/// ```
/// tmux::has_session("muxed".to_string());
/// => ExitStatus
/// ```
/// ```rust
/// extern crate load;
/// use load::tmux;
///
/// `target`: A string represented by the `{named_session}`
/// let session = tmux::has_session("muxed");
///
/// assert!(!session.success());
/// ```
pub fn has_session(target: &str) -> ExitStatus {
let output =
call(&["has-session", "-t", target]).expect("failed to see if the session existed");
Expand All @@ -53,9 +61,11 @@ pub fn has_session(target: &str) -> ExitStatus {
///
/// # Examples
///
/// ```
/// ```rust
/// extern crate load;
/// use load::tmux;
///
/// tmux::get_config();
/// => "some-option false\npane-base-index 0"
/// ```
pub fn get_config() -> String {
let output = call(&["start-server", ";", "show-options", "-g", ";", "show-options", "-g", "-w"])
Expand All @@ -69,11 +79,14 @@ pub fn get_config() -> String {
///
/// # Examples
///
/// ```
/// let session_name = "muxed".to_string();
/// tmux::attach(muxed);
/// ```
/// `session_name: The active tmux session name.
///
/// ```rust,no_run
/// extern crate load;
/// use load::tmux;
///
/// tmux::attach(&["muxed"]);
/// ```
pub fn attach(args: &[&str]) -> Result<Output, io::Error> {
let arg_string = [&[TMUX_NAME], &args[..]].concat().join(" ");
let system_call = CString::new(arg_string).unwrap();
Expand Down