aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2023-05-06 17:56:06 -0500
committerToby Vincent <tobyv13@gmail.com>2023-05-06 17:56:57 -0500
commitde5b70c5426311ce887e757c7397bed9107c83c0 (patch)
tree8da07e48b7e729122584a3681104396f51c5f54f /src
parentbcdcdb0ada8d13950e22909250b6b6783866535e (diff)
feat: impl tmux session source and parser
Diffstat (limited to 'src')
-rw-r--r--src/config.rs6
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs6
-rw-r--r--src/project.rs6
-rw-r--r--src/tmux.rs82
-rw-r--r--src/tmux/error.rs49
6 files changed, 148 insertions, 2 deletions
diff --git a/src/config.rs b/src/config.rs
index c45036c..d961f50 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -64,6 +64,12 @@ pub struct Projects {
#[cfg(feature = "git")]
#[arg(long, short)]
pub git: bool,
+
+ /// Add current tmux session directories.
+ ///
+ /// Uses last attached time as the timestamp.
+ #[arg(long, short)]
+ pub tmux: bool,
}
#[derive(Debug, Default, Clone, Args)]
diff --git a/src/lib.rs b/src/lib.rs
index af1a2fb..4e55141 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,3 +9,4 @@ pub mod search;
#[cfg(feature = "git")]
pub mod git;
pub mod path;
+pub mod tmux;
diff --git a/src/main.rs b/src/main.rs
index bdc7998..383411b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
use anyhow::Result;
use clap::Parser;
-use projectr::{config::Config, project::Projects, Search};
+use projectr::{config::Config, project::Projects, tmux::Tmux, Search};
fn main() -> Result<()> {
let config = Config::parse();
@@ -19,5 +19,9 @@ fn main() -> Result<()> {
projects.extend(search);
}
+ if let Ok(sessions) = Tmux::sessions() {
+ projects.extend(sessions)
+ }
+
Ok(projects.write(std::io::stdout())?)
}
diff --git a/src/project.rs b/src/project.rs
index 5f98ef6..d05da37 100644
--- a/src/project.rs
+++ b/src/project.rs
@@ -9,7 +9,7 @@ use std::{
use tracing::trace;
-use crate::{parser::FilterMap, path::PathMatcher};
+use crate::{parser::FilterMap, path::PathMatcher, tmux::Tmux};
#[derive(Default)]
pub struct Projects {
@@ -111,6 +111,10 @@ impl From<crate::config::Projects> for Projects {
projects.add_filter(PathMatcher(pattern.to_owned()));
}
+ if value.tmux {
+ projects.add_filter(Tmux);
+ }
+
#[cfg(feature = "git")]
if value.git {
projects.add_filter(crate::git::Git);
diff --git a/src/tmux.rs b/src/tmux.rs
new file mode 100644
index 0000000..559242b
--- /dev/null
+++ b/src/tmux.rs
@@ -0,0 +1,82 @@
+use std::{ffi::OsString, path::PathBuf, process::Command, time::Duration};
+
+use crate::{parser::Parser, project::Project};
+
+use self::error::Error;
+
+pub mod error;
+
+#[derive(Debug)]
+pub struct Tmux;
+
+impl Tmux {
+ pub fn sessions() -> Result<Vec<PathBuf>, Error> {
+ let stdout = Command::new("tmux")
+ .arg("list-sessions")
+ .arg("-F")
+ .arg("#{session_path}")
+ .output()?
+ .stdout;
+
+ Ok(std::str::from_utf8(&stdout)?
+ .lines()
+ .map(Into::into)
+ .collect())
+ }
+
+ pub fn get_session(&self, path_buf: PathBuf) -> Result<Project, Error> {
+ let mut filter = OsString::from("#{==:#{session_path},");
+ filter.push(path_buf.as_os_str());
+ filter.push("}");
+
+ // tmux list-sessions -f '#{==:#{session_path},/home/tobyv/src/projectr}'
+ let stdout = Command::new("tmux")
+ .arg("list-sessions")
+ .arg("-F")
+ .arg("#{session_path},#{session_last_attached},#{session_created}")
+ .arg("-f")
+ .arg(filter)
+ .output()?
+ .stdout;
+
+ std::str::from_utf8(&stdout)?
+ .lines()
+ .map(Self::parse_project)
+ .inspect(|res| {
+ if let Err(err) = res {
+ tracing::warn!(%err, "Skipping invalid format")
+ }
+ })
+ .flatten()
+ .max()
+ .ok_or(Error::NotFound)
+ }
+
+ fn parse_project<T: AsRef<str>>(fmt_str: T) -> Result<Project, Error> {
+ let mut split = fmt_str.as_ref().split(',');
+
+ let path_buf = split
+ .next()
+ .ok_or(Error::PathFormat(fmt_str.as_ref().to_string()))?
+ .into();
+
+ let timestamp = split
+ .next()
+ .ok_or(Error::TimestampFormat(fmt_str.as_ref().to_string()))?
+ .parse()
+ .map(Duration::from_secs)?;
+
+ Ok(Project {
+ timestamp,
+ path_buf,
+ })
+ }
+}
+
+impl Parser for Tmux {
+ type Error = Error;
+
+ fn parse(&self, path_buf: PathBuf) -> Result<Project, Self::Error> {
+ self.get_session(path_buf)
+ }
+}
diff --git a/src/tmux/error.rs b/src/tmux/error.rs
new file mode 100644
index 0000000..c857e5b
--- /dev/null
+++ b/src/tmux/error.rs
@@ -0,0 +1,49 @@
+#[derive(Debug)]
+pub enum FormatError {
+ Path(String),
+ Timestamp(String),
+}
+
+#[derive(Debug)]
+pub enum Error {
+ IO(std::io::Error),
+ Utf8(std::str::Utf8Error),
+ PathFormat(String),
+ TimestampFormat(String),
+ ParseInt(std::num::ParseIntError),
+ NotFound,
+}
+
+impl std::error::Error for Error {}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Error::IO(err) => write!(f, "IO error: {}", err),
+ Error::Utf8(err) => write!(f, "Failed to parse UTF8: {}", err),
+
+ Error::PathFormat(s) => write!(f, "Invalid path format: {}", s),
+ Error::TimestampFormat(s) => write!(f, "Invalid timestamp format: {}", s),
+ Error::ParseInt(err) => write!(f, "Failed to parse int: {}", err),
+ Error::NotFound => write!(f, "Tmux session not found"),
+ }
+ }
+}
+
+impl From<std::io::Error> for Error {
+ fn from(value: std::io::Error) -> Self {
+ Self::IO(value)
+ }
+}
+
+impl From<std::str::Utf8Error> for Error {
+ fn from(value: std::str::Utf8Error) -> Self {
+ Self::Utf8(value)
+ }
+}
+
+impl From<std::num::ParseIntError> for Error {
+ fn from(value: std::num::ParseIntError) -> Self {
+ Self::ParseInt(value)
+ }
+}