diff --git a/README.md b/README.md index 0e815c5..fb11f91 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Commands: clone Clones a Git repository to local delete Deletes a repository from local init Initialises a Git repository in local + open Opens a repository in an application path Prints the path to root, owner, or a repository profile Manages profiles to use in repositories shell Writes a shell script to extend ghr features diff --git a/src/application.rs b/src/application.rs new file mode 100644 index 0000000..92daa66 --- /dev/null +++ b/src/application.rs @@ -0,0 +1,59 @@ +use std::collections::HashMap; +use std::ops::Deref; +use std::path::Path; +use std::process::Command; + +use anyhow::{anyhow, Result}; +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct Application { + cmd: String, + args: Vec, +} + +impl Application { + pub fn open

(&self, path: P) -> Result<()> + where + P: AsRef, + { + let _ = Command::new(&self.cmd) + .args( + self.args + .iter() + .map(|arg| match arg.as_str() { + "%p" => path.as_ref().to_string_lossy().to_string(), + _ => arg.to_string(), + }) + .collect::>(), + ) + .spawn()?; + + Ok(()) + } +} + +#[derive(Debug, Default, Deserialize)] +pub struct Applications { + #[serde(flatten)] + map: HashMap, +} + +impl Applications { + pub fn open

(&self, name: &str, path: P) -> Result<()> + where + P: AsRef, + { + self.get(name) + .ok_or_else(|| anyhow!("Application entry does not exists."))? + .open(path) + } +} + +impl Deref for Applications { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.map + } +} diff --git a/src/cmd/clone.rs b/src/cmd/clone.rs index 6dab298..8b54f03 100644 --- a/src/cmd/clone.rs +++ b/src/cmd/clone.rs @@ -24,6 +24,10 @@ pub struct Cmd { /// Change directory after cloned a repository (Shell extension required). #[clap(long)] cd: bool, + + /// Opens the directory after cloned a repository. + #[clap(long)] + open: Option, } impl Cmd { @@ -51,11 +55,6 @@ impl Cmd { config.git.strategy.clone.clone_repository(url, &path)?; let repo = Repository::open(&path)?; - if let Some((name, p)) = profile { - p.apply(&mut repo.config()?)?; - - info!("Attached profile [{}] successfully.", style(name).bold()); - } tx.send(())?; progress.await?; @@ -65,6 +64,21 @@ impl Cmd { repo.workdir().unwrap().to_string_lossy(), ); + if let Some((name, p)) = profile { + p.apply(&mut repo.config()?)?; + + info!("Attached profile [{}] successfully.", style(name).bold()); + } + + if let Some(app) = self.open { + config.applications.open(&app, &path)?; + + info!( + "Opened the repository in [{}] successfully.", + style(&app).bold(), + ); + } + Ok(()) } } diff --git a/src/cmd/delete.rs b/src/cmd/delete.rs index 9ae96a3..8537a0a 100644 --- a/src/cmd/delete.rs +++ b/src/cmd/delete.rs @@ -16,7 +16,7 @@ use crate::url::Url; #[derive(Debug, Parser)] pub struct Cmd { - /// URL or pattern of the repository to clone. + /// URL or pattern of the repository to delete. repo: String, } diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index a76a029..4e1ea59 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -2,6 +2,7 @@ mod cd; mod clone; mod delete; mod init; +mod open; mod path; mod profile; mod shell; @@ -19,6 +20,8 @@ pub enum Action { Delete(delete::Cmd), /// Initialises a Git repository in local. Init(init::Cmd), + /// Opens a repository in an application. + Open(open::Cmd), /// Prints the path to root, owner, or a repository. Path(path::Cmd), /// Manages profiles to use in repositories. @@ -41,6 +44,7 @@ impl Cli { Clone(cmd) => cmd.run().await, Delete(cmd) => cmd.run().await, Init(cmd) => cmd.run(), + Open(cmd) => cmd.run(), Path(cmd) => cmd.run(), Profile(cmd) => cmd.run(), Shell(cmd) => cmd.run(), diff --git a/src/cmd/open.rs b/src/cmd/open.rs new file mode 100644 index 0000000..16f86ea --- /dev/null +++ b/src/cmd/open.rs @@ -0,0 +1,33 @@ +use std::path::PathBuf; +use std::str::FromStr; + +use anyhow::Result; +use clap::Parser; + +use crate::config::Config; +use crate::path::Path; +use crate::root::Root; +use crate::url::Url; + +#[derive(Debug, Parser)] +pub struct Cmd { + /// URL or pattern of the repository to open application in. + repo: String, + + /// Name of the application entry. + application: String, +} + +impl Cmd { + pub fn run(self) -> Result<()> { + let root = Root::find()?; + let config = Config::load_from(&root)?; + + let url = Url::from_str(&self.repo)?; + let path = PathBuf::from(Path::resolve(&root, &url)); + + config.applications.open(&self.application, path)?; + + Ok(()) + } +} diff --git a/src/config.rs b/src/config.rs index 12c8a31..5b2bacf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,6 +3,7 @@ use std::fs::read_to_string; use anyhow::Result; use serde::Deserialize; +use crate::application::Applications; use crate::git::Config as GitConfig; use crate::profile::Profiles; use crate::root::Root; @@ -15,6 +16,8 @@ pub struct Config { #[serde(default)] pub profiles: Profiles, #[serde(default)] + pub applications: Applications, + #[serde(default)] pub rules: Rules, } diff --git a/src/main.rs b/src/main.rs index 59bd3ef..43914e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod application; mod cmd; mod config; mod console;