diff --git a/README.md b/README.md index cdc4418..326bc62 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,17 @@ If you have installed the shell extension, you can change directory into the clo ghr clone --cd ``` +If you often use repositories of a specific owner, you can set the default owner to be resolved. + +```toml +[defaults] +owner = "siketyan" +``` + +```shell +ghr clone +``` + ### Changing directory You can change directory into a repository on the shell. It requires installing the shell extension. diff --git a/src/cmd/clone.rs b/src/cmd/clone.rs index 3e3007a..a10af62 100644 --- a/src/cmd/clone.rs +++ b/src/cmd/clone.rs @@ -1,5 +1,4 @@ use std::path::PathBuf; -use std::str::FromStr; use std::sync::mpsc::channel; use std::time::Duration; @@ -45,7 +44,7 @@ impl Cmd { p.finish_and_clear(); }); - let url = Url::from_str(&self.repo)?; + let url = Url::from_str(&self.repo, config.defaults.owner.as_deref())?; let path = PathBuf::from(Path::resolve(&root, &url)); let profile = config .rules diff --git a/src/cmd/delete.rs b/src/cmd/delete.rs index 8537a0a..60d2758 100644 --- a/src/cmd/delete.rs +++ b/src/cmd/delete.rs @@ -1,8 +1,8 @@ use std::path::PathBuf; -use std::str::FromStr; use std::sync::mpsc::channel; use std::time::Duration; +use crate::config::Config; use anyhow::Result; use clap::Parser; use console::style; @@ -23,6 +23,7 @@ pub struct Cmd { impl Cmd { pub async fn run(self) -> Result<()> { let root = Root::find()?; + let config = Config::load_from(&root)?; if !Confirm::new() .with_prompt(format!( @@ -34,7 +35,7 @@ impl Cmd { return Ok(()); } - let url = Url::from_str(&self.repo)?; + let url = Url::from_str(&self.repo, config.defaults.owner.as_deref())?; let path = PathBuf::from(Path::resolve(&root, &url)); let (tx, rx) = channel(); diff --git a/src/cmd/init.rs b/src/cmd/init.rs index c111940..582cf57 100644 --- a/src/cmd/init.rs +++ b/src/cmd/init.rs @@ -1,5 +1,4 @@ use std::path::PathBuf; -use std::str::FromStr; use anyhow::Result; use clap::Parser; @@ -32,7 +31,7 @@ impl Cmd { let root = Root::find()?; let config = Config::load_from(&root)?; - let url = Url::from_str(&self.repo)?; + let url = Url::from_str(&self.repo, config.defaults.owner.as_deref())?; let path = Path::resolve(&root, &url); let profile = config .rules diff --git a/src/cmd/open.rs b/src/cmd/open.rs index 49eace5..3913dc3 100644 --- a/src/cmd/open.rs +++ b/src/cmd/open.rs @@ -1,5 +1,4 @@ use std::path::PathBuf; -use std::str::FromStr; use anyhow::Result; use clap::Parser; @@ -23,7 +22,7 @@ impl Cmd { let root = Root::find()?; let config = Config::load_from(&root)?; - let url = Url::from_str(&self.repo)?; + let url = Url::from_str(&self.repo, config.defaults.owner.as_deref())?; let path = PathBuf::from(Path::resolve(&root, &url)); config diff --git a/src/config.rs b/src/config.rs index 53e3ca7..17a5c6a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,8 +10,15 @@ use crate::profile::Profiles; use crate::root::Root; use crate::rule::Rules; +#[derive(Debug, Default, Deserialize)] +pub struct Defaults { + pub owner: Option, +} + #[derive(Debug, Default, Deserialize)] pub struct Config { + #[serde(default)] + pub defaults: Defaults, #[serde(default)] pub git: GitConfig, #[serde(default)] diff --git a/src/url.rs b/src/url.rs index cf9a125..fb81939 100644 --- a/src/url.rs +++ b/src/url.rs @@ -92,6 +92,13 @@ pub struct Url { } impl Url { + pub fn from_str(s: &str, default_owner: Option<&str>) -> Result { + match s.contains("://") { + true => Self::from_url(&url::Url::from_str(s)?), + _ => Self::from_pattern(s, default_owner), + } + } + fn from_url(url: &url::Url) -> Result { let mut segments = url .path_segments() @@ -120,8 +127,11 @@ impl Url { }) } - fn from_pattern(s: &str) -> Result { + fn from_pattern(s: &str, default_owner: Option<&str>) -> Result { lazy_static! { + static ref REPO: Regex = + Regex::new(r"^(?P[0-9A-Za-z_\.\-]+)$").unwrap(); + static ref ORG_REPO: Regex = Regex::new(r"^(?P[0-9A-Za-z_\.\-]+)/(?P[0-9A-Za-z_\.\-]+)$").unwrap(); @@ -152,6 +162,14 @@ impl Url { }; } + if let Some(owner) = default_owner { + pattern!(REPO, |c: Captures| Ok(Self { + owner: owner.to_string(), + repo: Self::remove_extensions(&group!(c, "repo")), + ..Default::default() + })); + } + pattern!(ORG_REPO, |c: Captures| Ok(Self { owner: group!(c, "org"), repo: Self::remove_extensions(&group!(c, "repo")), @@ -195,10 +213,7 @@ impl FromStr for Url { type Err = Error; fn from_str(s: &str) -> Result { - match s.contains("://") { - true => Self::from_url(&url::Url::from_str(s)?), - _ => Self::from_pattern(s), - } + Url::from_str(s, None) } } @@ -270,6 +285,21 @@ mod tests { ) } + #[test] + fn parse_from_pattern_repo() { + assert_eq!( + Url { + vcs: Vcs::Git, + scheme: Scheme::Https, + user: None, + host: Host::GitHub, + owner: "siketyan".to_string(), + repo: "siketyan.github.io".to_string() + }, + Url::from_pattern("siketyan.github.io", Some("siketyan")).unwrap(), + ) + } + #[test] fn parse_from_pattern_org_repo() { assert_eq!( @@ -281,7 +311,7 @@ mod tests { owner: "siketyan".to_string(), repo: "siketyan.github.io".to_string() }, - Url::from_pattern("siketyan/siketyan.github.io").unwrap(), + Url::from_pattern("siketyan/siketyan.github.io", None).unwrap(), ) } @@ -296,7 +326,7 @@ mod tests { owner: "siketyan".to_string(), repo: "siketyan.github.io".to_string() }, - Url::from_pattern("gitlab.com:siketyan/siketyan.github.io").unwrap(), + Url::from_pattern("gitlab.com:siketyan/siketyan.github.io", None).unwrap(), ) } @@ -311,7 +341,7 @@ mod tests { owner: "siketyan".to_string(), repo: "siketyan.github.io".to_string() }, - Url::from_pattern("git@github.com:siketyan/siketyan.github.io.git").unwrap(), + Url::from_pattern("git@github.com:siketyan/siketyan.github.io.git", None).unwrap(), ) }