1use clap::Parser;
2
3use crate::constants;
4
5#[derive(Parser, Debug)]
6#[command(
7 name = "wtg",
8 version,
9 about = constants::DESCRIPTION,
10 disable_help_flag = true,
11)]
12pub struct Cli {
13 #[arg(value_name = "COMMIT|ISSUE|FILE|TAG|URL")]
15 pub input: Option<String>,
16
17 #[arg(short = 'r', long, value_name = "URL")]
19 pub repo: Option<String>,
20
21 #[arg(short, long, action = clap::ArgAction::Help)]
23 help: Option<bool>,
24}
25
26#[derive(Debug, Clone)]
28pub struct ParsedInput {
29 pub owner: Option<String>,
30 pub repo: Option<String>,
31 pub query: String,
32}
33
34impl Cli {
35 #[must_use]
37 pub fn parse_input(&self) -> Option<ParsedInput> {
38 let input = self.input.as_ref()?;
39
40 if let Some(repo_url) = &self.repo {
42 let (owner, repo) = parse_github_repo_url(repo_url)?;
43 return Some(ParsedInput {
44 owner: Some(owner),
45 repo: Some(repo),
46 query: input.clone(),
47 });
48 }
49
50 if let Some(parsed) = parse_github_url(input) {
52 return Some(parsed);
53 }
54
55 Some(ParsedInput {
57 owner: None,
58 repo: None,
59 query: input.clone(),
60 })
61 }
62}
63
64fn parse_github_url(url: &str) -> Option<ParsedInput> {
72 if !url.contains("github.com") {
73 return None;
74 }
75
76 let path = if url.starts_with("git@") {
78 url.split(':').nth(1)?
80 } else {
81 url.split("github.com/").nth(1)?
83 };
84
85 let path = path.trim_end_matches(".git");
86 let parts: Vec<&str> = path.split('/').collect();
87
88 if parts.len() < 2 {
89 return None;
90 }
91
92 let owner = parts[0].to_string();
93 let repo = parts[1].to_string();
94
95 let query = if parts.len() == 2 {
97 return None;
99 } else if parts.len() >= 4 {
100 match parts[2] {
101 "commit" => parts[3].to_string(),
102 "issues" | "pull" => format!("#{}", parts[3]),
103 "blob" | "tree" => {
104 if parts.len() >= 5 {
107 parts[4..].join("/")
108 } else {
109 return None;
110 }
111 }
112 _ => return None,
113 }
114 } else {
115 return None;
116 };
117
118 Some(ParsedInput {
119 owner: Some(owner),
120 repo: Some(repo),
121 query,
122 })
123}
124
125fn parse_github_repo_url(url: &str) -> Option<(String, String)> {
127 if url.starts_with("git@") {
129 let path = url.split(':').nth(1)?;
130 let path = path.trim_end_matches(".git");
131 let parts: Vec<&str> = path.split('/').collect();
132 if parts.len() >= 2 {
133 return Some((parts[0].to_string(), parts[1].to_string()));
134 }
135 return None;
136 }
137
138 if url.contains("github.com/") {
140 let path = url.split("github.com/").nth(1)?;
141 let path = path.trim_end_matches(".git");
142 let parts: Vec<&str> = path.split('/').collect();
143 if parts.len() >= 2 {
144 return Some((parts[0].to_string(), parts[1].to_string()));
145 }
146 return None;
147 }
148
149 let parts: Vec<&str> = url.split('/').collect();
151 if parts.len() == 2 {
152 return Some((parts[0].to_string(), parts[1].to_string()));
153 }
154
155 None
156}