11use std:: collections:: HashMap ;
22use std:: path:: { Path , PathBuf } ;
3+ use std:: process:: Command ;
34
45use crate :: error:: { DicecutError , Result } ;
56
@@ -20,7 +21,44 @@ const ABBREVIATIONS: &[(&str, &str, &str)] = &[
2021 ( "sr:" , "https://git.sr.ht/" , "" ) ,
2122] ;
2223
24+ fn detect_github_protocol ( ) -> String {
25+ Command :: new ( "gh" )
26+ . args ( [ "config" , "get" , "git_protocol" , "-h" , "github.com" ] )
27+ . output ( )
28+ . ok ( )
29+ . and_then ( |output| {
30+ if output. status . success ( ) {
31+ let stdout = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
32+ if stdout == "ssh" {
33+ return Some ( "ssh" . to_string ( ) ) ;
34+ }
35+ }
36+ None
37+ } )
38+ . unwrap_or_else ( || "https" . to_string ( ) )
39+ }
40+
41+ fn build_github_url ( rest : & str , protocol : & str ) -> String {
42+ if protocol == "ssh" {
43+ format ! ( "git@github.com:{rest}.git" )
44+ } else {
45+ format ! ( "https://github.com/{rest}.git" )
46+ }
47+ }
48+
2349fn expand_abbreviation ( input : & str ) -> Result < String > {
50+ // Special case: GitHub abbreviation with protocol detection
51+ if let Some ( rest) = input. strip_prefix ( "gh:" ) {
52+ if rest. is_empty ( ) {
53+ return Err ( DicecutError :: InvalidAbbreviation {
54+ input : input. to_string ( ) ,
55+ } ) ;
56+ }
57+ let protocol = detect_github_protocol ( ) ;
58+ return Ok ( build_github_url ( rest, & protocol) ) ;
59+ }
60+
61+ // All other abbreviations use static expansion
2462 for & ( prefix, base_url, suffix) in ABBREVIATIONS {
2563 if let Some ( rest) = input. strip_prefix ( prefix) {
2664 if rest. is_empty ( ) {
@@ -129,10 +167,25 @@ mod tests {
129167
130168 // ── Abbreviation expansion ──────────────────────────────────────────
131169
170+ #[ test]
171+ fn build_github_url_ssh ( ) {
172+ let url = build_github_url ( "user/repo" , "ssh" ) ;
173+ assert_eq ! ( url, "git@github.com:user/repo.git" ) ;
174+ }
175+
176+ #[ test]
177+ fn build_github_url_https ( ) {
178+ let url = build_github_url ( "user/repo" , "https" ) ;
179+ assert_eq ! ( url, "https://github.com/user/repo.git" ) ;
180+ }
181+
132182 #[ test]
133183 fn expand_github_abbreviation ( ) {
134184 let url = expand_abbreviation ( "gh:user/repo" ) . unwrap ( ) ;
135- assert_eq ! ( url, "https://github.com/user/repo.git" ) ;
185+ assert ! (
186+ url == "https://github.com/user/repo.git" || url == "git@github.com:user/repo.git" ,
187+ "unexpected URL: {url}"
188+ ) ;
136189 }
137190
138191 #[ test]
@@ -191,7 +244,11 @@ mod tests {
191244 let source = resolve_source ( "gh:user/repo" ) . unwrap ( ) ;
192245 match source {
193246 TemplateSource :: Git { url, git_ref } => {
194- assert_eq ! ( url, "https://github.com/user/repo.git" ) ;
247+ assert ! (
248+ url == "https://github.com/user/repo.git"
249+ || url == "git@github.com:user/repo.git" ,
250+ "unexpected URL: {url}"
251+ ) ;
195252 assert ! ( git_ref. is_none( ) ) ;
196253 }
197254 _ => panic ! ( "expected Git source" ) ,
@@ -229,7 +286,11 @@ mod tests {
229286 let source = resolve_source_with_ref ( "gh:user/repo" , Some ( "v1.0" ) ) . unwrap ( ) ;
230287 match source {
231288 TemplateSource :: Git { url, git_ref } => {
232- assert_eq ! ( url, "https://github.com/user/repo.git" ) ;
289+ assert ! (
290+ url == "https://github.com/user/repo.git"
291+ || url == "git@github.com:user/repo.git" ,
292+ "unexpected URL: {url}"
293+ ) ;
233294 assert_eq ! ( git_ref. as_deref( ) , Some ( "v1.0" ) ) ;
234295 }
235296 _ => panic ! ( "expected Git source" ) ,
@@ -241,7 +302,11 @@ mod tests {
241302 let source = resolve_source_with_ref ( "gh:user/repo" , None ) . unwrap ( ) ;
242303 match source {
243304 TemplateSource :: Git { url, git_ref } => {
244- assert_eq ! ( url, "https://github.com/user/repo.git" ) ;
305+ assert ! (
306+ url == "https://github.com/user/repo.git"
307+ || url == "git@github.com:user/repo.git" ,
308+ "unexpected URL: {url}"
309+ ) ;
245310 assert ! ( git_ref. is_none( ) ) ;
246311 }
247312 _ => panic ! ( "expected Git source" ) ,
@@ -342,7 +407,11 @@ mod tests {
342407 let source = resolve_source_full ( "gh:user/repo" , None , Some ( & abbrevs) ) . unwrap ( ) ;
343408 match source {
344409 TemplateSource :: Git { url, .. } => {
345- assert_eq ! ( url, "https://github.com/user/repo.git" ) ;
410+ assert ! (
411+ url == "https://github.com/user/repo.git"
412+ || url == "git@github.com:user/repo.git" ,
413+ "unexpected URL: {url}"
414+ ) ;
346415 }
347416 _ => panic ! ( "expected Git source" ) ,
348417 }
@@ -353,7 +422,11 @@ mod tests {
353422 let source = resolve_source_full ( "gh:user/repo" , None , None ) . unwrap ( ) ;
354423 match source {
355424 TemplateSource :: Git { url, .. } => {
356- assert_eq ! ( url, "https://github.com/user/repo.git" ) ;
425+ assert ! (
426+ url == "https://github.com/user/repo.git"
427+ || url == "git@github.com:user/repo.git" ,
428+ "unexpected URL: {url}"
429+ ) ;
357430 }
358431 _ => panic ! ( "expected Git source" ) ,
359432 }
0 commit comments