aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs81
-rw-r--r--src/config.rs114
-rw-r--r--src/error.rs10
-rw-r--r--src/lib.rs12
-rw-r--r--src/main.rs13
5 files changed, 228 insertions, 2 deletions
diff --git a/src/cli.rs b/src/cli.rs
new file mode 100644
index 0000000..5da1036
--- /dev/null
+++ b/src/cli.rs
@@ -0,0 +1,81 @@
+use crate::{Config, Result};
+use clap::{Args, Parser};
+use std::fs::File;
+use std::sync::Arc;
+use tracing_subscriber::{filter::LevelFilter, Layer, Registry};
+
+/// Simple program to manage projects and ssh hosts using tmux
+#[derive(Parser, Debug)]
+#[command(author, version, about)]
+pub struct Cli {
+ #[command(flatten)]
+ pub verbose: Verbosity,
+
+ /// Connect to ssh host
+ #[arg(short, long)]
+ pub ssh: Option<String>,
+
+ #[command(flatten)]
+ pub config: Config,
+}
+
+impl Cli {
+ pub fn as_layer(&self) -> Result<Vec<Box<dyn Layer<Registry> + Send + Sync>>> {
+ let mut layers = Vec::new();
+
+ let fmt_layer = tracing_subscriber::fmt::layer()
+ .pretty()
+ .with_filter(self.verbose.into_filter())
+ .boxed();
+
+ layers.push(fmt_layer);
+
+ if self.config.enable_logging {
+ let file = File::create(&self.config.log_file)?;
+ let log_layer = tracing_subscriber::fmt::layer()
+ .with_writer(Arc::new(file))
+ .boxed();
+ layers.push(log_layer);
+ };
+
+ Ok(layers)
+ }
+}
+
+#[derive(Debug, Default, Args)]
+pub struct Verbosity {
+ /// Print additional information per occurrence
+ #[arg(short, long, action = clap::ArgAction::Count, conflicts_with = "quiet")]
+ pub verbose: u8,
+
+ /// Suppress all output
+ #[arg(short, long, global = true, conflicts_with = "verbose")]
+ pub quiet: bool,
+}
+
+impl Verbosity {
+ pub fn into_filter(&self) -> LevelFilter {
+ self.into()
+ }
+}
+
+impl From<&Verbosity> for LevelFilter {
+ fn from(value: &Verbosity) -> Self {
+ match value.verbose + 1 - u8::from(value.quiet) {
+ 0 => LevelFilter::OFF,
+ 1 => LevelFilter::ERROR,
+ 2 => LevelFilter::WARN,
+ 3 => LevelFilter::INFO,
+ 4 => LevelFilter::DEBUG,
+ _ => LevelFilter::TRACE,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_start() {
+ assert_eq!(1, 1);
+ }
+}
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..42614ec
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,114 @@
+use crate::{Error, Result};
+use clap::{Args, Parser};
+use figment::{
+ providers::{Env, Format, Serialized, Toml},
+ Figment,
+};
+use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
+
+/// Simple program to manage projects and ssh hosts using tmux
+#[derive(Debug, Parser, Serialize, Deserialize)]
+#[command(author, version, about)]
+pub struct Config {
+ /// Path to search for directories
+ pub(crate) path: Vec<PathBuf>,
+
+ /// Stand alone directory. Can be specified multiple times
+ #[arg(short, long)]
+ pub(crate) project: Vec<PathBuf>,
+
+ /// Allows traversal into hidden directories when searching
+ #[arg(long)]
+ pub(crate) hidden: bool,
+
+ #[arg(skip)]
+ pub(crate) finder: Finder,
+
+ #[arg(skip)]
+ pub(crate) enable_logging: bool,
+
+ #[arg(skip)]
+ pub(crate) log_file: PathBuf,
+}
+
+impl Config {
+ pub fn extract(&self) -> Result<Config> {
+ Figment::from(Serialized::defaults(self))
+ .merge(Toml::file("tmuxr.toml"))
+ .merge(Env::prefixed("TMUXR_"))
+ .extract()
+ .map_err(Error::from)
+ }
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ path: Default::default(),
+ project: Default::default(),
+ hidden: Default::default(),
+ finder: Default::default(),
+ enable_logging: Default::default(),
+ log_file: dirs::cache_dir()
+ .map(|p| p.join("tmuxr"))
+ .unwrap_or_default()
+ .join("tmuxr.log"),
+ }
+ }
+}
+
+#[derive(Debug, Args, Serialize, Deserialize)]
+pub(crate) struct Finder {
+ pub(crate) program: String,
+ pub(crate) args: Vec<String>,
+}
+
+impl Default for Finder {
+ fn default() -> Self {
+ Self {
+ program: "fzf-tmux".into(),
+ args: vec![
+ "--".into(),
+ "--multi".into(),
+ "--print-query".into(),
+ "-d/".into(),
+ "--preview-window='right,75%,<80(up,75%,border-bottom)'".into(),
+ "--preview='sel={}; less ${sel:-{q}} 2>/dev/null'".into(),
+ ],
+ }
+ }
+}
+
+#[derive(Debug, Args, Serialize, Deserialize)]
+pub(crate) struct Directory {
+ pub(crate) path: PathBuf,
+ pub(crate) root: bool,
+ pub(crate) hidden: bool,
+}
+
+#[derive(Debug, Default)]
+pub(crate) struct Directories {
+ search_dirs: Vec<PathBuf>,
+ root_dirs: Vec<PathBuf>,
+}
+
+impl From<Vec<Directory>> for Directories {
+ fn from(value: Vec<Directory>) -> Self {
+ value.iter().fold(Directories::default(), |mut acc, d| {
+ match d.root {
+ true => acc.root_dirs.push(d.path.to_owned()),
+ false => acc.search_dirs.push(d.path.to_owned()),
+ };
+ acc
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_start() {
+ assert_eq!(1, 1);
+ }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..e423493
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,10 @@
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error("IO error: {0:?}")]
+ IO(#[from] std::io::Error),
+
+ #[error("Config error: {0:?}")]
+ Config(#[from] figment::error::Error),
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..872ca50
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,12 @@
+pub use crate::cli::Cli;
+pub use crate::config::Config;
+pub use crate::error::{Error, Result};
+
+mod cli;
+mod config;
+mod error;
+
+#[tracing::instrument()]
+pub fn run(config: &Config) -> Result<()> {
+ Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index e7a11a9..40fccd6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,12 @@
-fn main() {
- println!("Hello, world!");
+use clap::Parser;
+use tmuxr::{Cli, Result};
+use tracing_subscriber::prelude::*;
+
+fn main() -> Result<()> {
+ let cli = Cli::parse();
+ let config = cli.config.extract()?;
+
+ tracing_subscriber::registry().with(cli.as_layer()?).init();
+
+ tmuxr::run(&config)
}