Skip to content

Commit ed998df

Browse files
Extract library to interpret CLI prompts (libprompt)
1 parent c6da945 commit ed998df

File tree

5 files changed

+176
-291
lines changed

5 files changed

+176
-291
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ GH_DIR=~/.local/bin
44
MAN_DIR=~/.local/man/man1
55

66
build_tests:
7-
$(CC) $(CC_FLAGS) tests.out ./tests/*.c ./src/gh.c -lcmocka -Wl,--wrap=find_git_config -DTESTING
7+
$(CC) $(CC_FLAGS) tests.out ./tests/*.c ./src/gh.c -lprompt -lcmocka -Wl,--wrap=find_git_config -DTESTING
88
install:
99
mkdir -p $(GH_DIR)
1010
mkdir -p $(MAN_DIR)
11-
$(CC) $(CC_FLAGS) $(GH_DIR)/gh ./src/*.c
11+
$(CC) $(CC_FLAGS) $(GH_DIR)/gh ./src/*.c -lprompt
1212
cp ./docs/gh.1 $(MAN_DIR)/gh.1
1313
gzip $(MAN_DIR)/gh.1
1414
uninstall:

src/gh.c

Lines changed: 95 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,67 @@
11
#include "gh.h"
22

33
static int parse_cmd(char *arg[]);
4-
static int is_a_opt(char *opt);
5-
static struct Option parse_opt(char *opt);
64
static int generate_git_remote_url(char *url);
75
static void find_git_remote_url(FILE *git_config, char *buff);
86
static void adapt_git_remote_url(char *url);
97
static char *generate_firefox_instruction(char *url);
10-
static void close_firefox_argument(struct Prompt *p);
11-
static void handle_pulls_options(struct Prompt *p);
12-
static void filter_open_prs(struct Prompt *p);
13-
static void filter_closed_prs(struct Prompt *p);
14-
static void filter_prs_by_author(struct Prompt *p, char *author);
15-
static void filter_prs_to_review(struct Prompt *p);
16-
static void handle_newpr_options(struct Prompt *p);
17-
static void set_destination_and_source(struct Prompt *p, char *dest_src);
18-
static void set_template(struct Prompt *p, char *template);
19-
static void set_title(struct Prompt *p, char *title);
20-
static void set_assignees(struct Prompt *p, char *assignees);
21-
static void set_labels(struct Prompt *p, char *labels);
22-
static void assure_query_param_support(struct Prompt *p);
23-
static void warn_missing_branches(struct Prompt *p);
24-
25-
struct Prompt parse_prompt(int argc, char *argv[]) {
26-
struct Prompt result = { DEFAULT_CMD };
27-
28-
// The first arg will be `gh`, so we can skip it.
29-
int opt_i = -1;
30-
for (int i = 1; i < argc; i++) {
31-
if (i == 1) {
32-
result.cmd = parse_cmd(&argv[i]);
33-
} else if (is_a_opt(argv[i])) {
34-
opt_i++;
35-
result.opts[opt_i] = parse_opt(argv[i]);
36-
} else {
37-
strcat(result.opts[opt_i].value, "+");
38-
strcat(result.opts[opt_i].value, argv[i]);
39-
}
40-
}
41-
42-
return result;
43-
}
44-
45-
static int is_a_opt(char *opt) {
46-
return strstr(opt, "--") != NULL;
47-
}
48-
49-
static int parse_cmd(char *arg[]) {
8+
static void close_firefox_argument(struct Context *c);
9+
static void handle_pulls_options(struct Context *c);
10+
static void filter_open_prs(struct Context *c);
11+
static void filter_closed_prs(struct Context *c);
12+
static void filter_prs_by_author(struct Context *c, char *author);
13+
static void filter_prs_to_review(struct Context *c);
14+
static void handle_newpr_options(struct Context *c);
15+
static void set_destination_and_source(struct Context *c, char *dest_src);
16+
static void set_template(struct Context *c, char *template);
17+
static void set_title(struct Context *c, char *title);
18+
static void set_assignees(struct Context *c, char *assignees);
19+
static void set_labels(struct Context *c, char *labels);
20+
static void assure_query_param_support(struct Context *c);
21+
static void warn_missing_branches(struct Context *c);
22+
23+
int cmd_table(char *cmd_str) {
5024
int result;
5125

52-
if (strcmp(*arg, "help") == 0)
26+
if (compare_command(cmd_str, "help") ||
27+
compare_command(cmd_str, "-h") ||
28+
compare_command(cmd_str, "--help"))
5329
result = HELP_CMD;
54-
else if (strcmp(*arg, "repo") == 0)
30+
else if (compare_command(cmd_str, "repo"))
5531
result = REPO_CMD;
56-
else if (strcmp(*arg, "pulls") == 0)
32+
else if (compare_command(cmd_str, "pulls"))
5733
result = PULLS_CMD;
58-
else if (strcmp(*arg, "newpr") == 0)
34+
else if (compare_command(cmd_str, "newpr"))
5935
result = NEWPR_CMD;
6036
else
6137
result = INVALID_CMD;
6238

6339
return result;
6440
}
6541

66-
static struct Option parse_opt(char *opt) {
67-
struct Option o;
68-
69-
char received_opt[MAX_STR_SIZE];
70-
strcpy(received_opt, opt);
71-
size_t opt_sz = strlen(received_opt);
72-
73-
char *value_with_sep = memchr(received_opt, '=', opt_sz);
74-
if (value_with_sep != NULL) {
75-
char *only_value = value_with_sep+1;
76-
strcpy(o.value, only_value);
77-
char *break_line = memchr(o.value, '\n', strlen(o.value));
78-
if (break_line != NULL)
79-
memset(break_line, '\0', 1);
80-
81-
memset(value_with_sep, '\0', 1);
82-
}
83-
84-
strcpy(o.key, received_opt);
85-
86-
return o;
87-
}
88-
89-
void add_instruction(struct Prompt *prompt) {
90-
if (prompt->cmd == HELP_CMD) {
91-
strcpy(prompt->instruction, "man gh");
42+
void add_instruction(struct Context *context) {
43+
if (context->prompt.cmd == HELP_CMD) {
44+
strcpy(context->instruction, "man gh");
9245
return;
9346
}
9447

9548
char remote_url[MAX_STR_SIZE];
9649
int success = generate_git_remote_url(remote_url);
9750
if (!success) {
98-
strcpy(prompt->error, "Repository configuration not found.");
51+
strcpy(context->error, "Repository configuration not found.");
9952
return;
10053
}
101-
strcpy(prompt->instruction, generate_firefox_instruction(remote_url));
54+
strcpy(context->instruction, generate_firefox_instruction(remote_url));
10255

103-
if (prompt->cmd == PULLS_CMD) strcat(prompt->instruction, "/pulls?q=is:pr");
104-
else if (prompt->cmd == NEWPR_CMD) strcat(prompt->instruction, "/compare");
56+
if (context->prompt.cmd == PULLS_CMD) strcat(context->instruction, "/pulls?q=is:pr");
57+
else if (context->prompt.cmd == NEWPR_CMD) strcat(context->instruction, "/compare");
10558

106-
if (prompt->opts[0].key[0] != '\0') {
107-
handle_pulls_options(prompt);
108-
handle_newpr_options(prompt);
59+
if (context->prompt.opts[0].key[0] != '\0') {
60+
handle_pulls_options(context);
61+
handle_newpr_options(context);
10962
}
11063

111-
close_firefox_argument(prompt);
64+
close_firefox_argument(context);
11265
}
11366

11467
static int generate_git_remote_url(char *url) {
@@ -159,74 +112,74 @@ static char *generate_firefox_instruction(char *url) {
159112
return strcat(firefox_bin_cmd, url);
160113
}
161114

162-
static void close_firefox_argument(struct Prompt *p) {
163-
size_t sz = strlen(p->instruction);
164-
if (p->instruction[sz - 1] == '\'') return;
115+
static void close_firefox_argument(struct Context *c) {
116+
size_t sz = strlen(c->instruction);
117+
if (c->instruction[sz - 1] == '\'') return;
165118

166-
strcat(p->instruction, "'");
119+
strcat(c->instruction, "'");
167120
}
168121

169-
static void handle_pulls_options(struct Prompt *p) {
170-
if (p->cmd != PULLS_CMD) return;
122+
static void handle_pulls_options(struct Context *c) {
123+
if (c->prompt.cmd != PULLS_CMD) return;
171124

172125
for (int i = 0; i < MAX_CMD_OPTS; i++) {
173-
if (strcmp(p->opts[i].key, "--open") == 0) {
174-
filter_open_prs(p);
175-
} else if (strcmp(p->opts[i].key, "--closed") == 0) {
176-
filter_closed_prs(p);
177-
} else if (strcmp(p->opts[i].key, "--author") == 0) {
178-
filter_prs_by_author(p, p->opts[i].value);
179-
} else if (strcmp(p->opts[i].key, "--to-review") == 0) {
180-
filter_open_prs(p);
181-
filter_prs_to_review(p);
126+
if (strcmp(c->prompt.opts[i].key, "--open") == 0) {
127+
filter_open_prs(c);
128+
} else if (strcmp(c->prompt.opts[i].key, "--closed") == 0) {
129+
filter_closed_prs(c);
130+
} else if (strcmp(c->prompt.opts[i].key, "--author") == 0) {
131+
filter_prs_by_author(c, c->prompt.opts[i].value);
132+
} else if (strcmp(c->prompt.opts[i].key, "--to-review") == 0) {
133+
filter_open_prs(c);
134+
filter_prs_to_review(c);
182135
}
183136
}
184137
}
185138

186-
static void filter_open_prs(struct Prompt *p) {
187-
strcat(p->instruction, "+is:open");
139+
static void filter_open_prs(struct Context *c) {
140+
strcat(c->instruction, "+is:open");
188141
}
189142

190-
static void filter_closed_prs(struct Prompt *p) {
191-
strcat(p->instruction, "+is:closed");
143+
static void filter_closed_prs(struct Context *c) {
144+
strcat(c->instruction, "+is:closed");
192145
}
193146

194-
static void filter_prs_by_author(struct Prompt *p, char *author) {
147+
static void filter_prs_by_author(struct Context *c, char *author) {
195148
char author_param[MAX_STR_SIZE];
196149
strcpy(author_param, "+author:");
197150
strcat(author_param, author);
198-
strcat(p->instruction, author_param);
151+
strcat(c->instruction, author_param);
199152
}
200153

201-
static void filter_prs_to_review(struct Prompt *p) {
202-
strcat(p->instruction, "+user-review-requested:@me");
154+
static void filter_prs_to_review(struct Context *c) {
155+
strcat(c->instruction, "+user-review-requested:@me");
203156
}
204157

205-
static void handle_newpr_options(struct Prompt *p) {
206-
if (p->cmd != NEWPR_CMD) return;
158+
static void handle_newpr_options(struct Context *c) {
159+
if (c->prompt.cmd != NEWPR_CMD) return;
207160

208161
for (int i = 0; i < MAX_CMD_OPTS; i++) {
209-
if (strcmp(p->opts[i].key, "--dest-src") == 0) {
210-
set_destination_and_source(p, p->opts[i].value);
211-
} else if (strcmp(p->opts[i].key, "--template") == 0) {
212-
set_template(p, p->opts[i].value);
213-
} else if (strcmp(p->opts[i].key, "--title") == 0) {
214-
set_title(p, p->opts[i].value);
215-
} else if (strcmp(p->opts[i].key, "--assignees") == 0) {
216-
set_assignees(p, p->opts[i].value);
217-
} else if (strcmp(p->opts[i].key, "--labels") == 0) {
218-
set_labels(p, p->opts[i].value);
162+
if (strcmp(c->prompt.opts[i].key, "--dest-src") == 0) {
163+
set_destination_and_source(c, c->prompt.opts[i].value);
164+
} else if (strcmp(c->prompt.opts[i].key, "--template") == 0) {
165+
set_template(c, c->prompt.opts[i].value);
166+
} else if (strcmp(c->prompt.opts[i].key, "--title") == 0) {
167+
set_title(c, c->prompt.opts[i].value);
168+
} else if (strcmp(c->prompt.opts[i].key, "--assignees") == 0) {
169+
set_assignees(c, c->prompt.opts[i].value);
170+
} else if (strcmp(c->prompt.opts[i].key, "--labels") == 0) {
171+
set_labels(c, c->prompt.opts[i].value);
219172
}
220173
}
221174

222-
warn_missing_branches(p);
175+
warn_missing_branches(c);
223176
}
224177

225-
static void set_destination_and_source(struct Prompt *p, char *dest_src) {
226-
char *query_params = memchr(p->instruction, '?', strlen(p->instruction));
178+
static void set_destination_and_source(struct Context *c, char *dest_src) {
179+
char *query_params = memchr(c->instruction, '?', strlen(c->instruction));
227180
if (query_params == NULL) {
228-
strcat(p->instruction, "/");
229-
strcat(p->instruction, dest_src);
181+
strcat(c->instruction, "/");
182+
strcat(c->instruction, dest_src);
230183
} else {
231184
char dest_src_param[MAX_STR_SIZE];
232185
dest_src_param[0] = '/';
@@ -238,42 +191,42 @@ static void set_destination_and_source(struct Prompt *p, char *dest_src) {
238191
}
239192
}
240193

241-
static void set_template(struct Prompt *p, char *template) {
242-
assure_query_param_support(p);
194+
static void set_template(struct Context *c, char *template) {
195+
assure_query_param_support(c);
243196

244-
strcat(p->instruction, "&template=");
245-
strcat(p->instruction, template);
197+
strcat(c->instruction, "&template=");
198+
strcat(c->instruction, template);
246199
}
247200

248-
static void set_title(struct Prompt *p, char *title) {
249-
assure_query_param_support(p);
201+
static void set_title(struct Context *c, char *title) {
202+
assure_query_param_support(c);
250203

251-
strcat(p->instruction, "&title=");
252-
strcat(p->instruction, title);
204+
strcat(c->instruction, "&title=");
205+
strcat(c->instruction, title);
253206
}
254207

255-
static void set_assignees(struct Prompt *p, char *assignees) {
256-
assure_query_param_support(p);
208+
static void set_assignees(struct Context *c, char *assignees) {
209+
assure_query_param_support(c);
257210

258-
strcat(p->instruction, "&assignees=");
259-
strcat(p->instruction, assignees);
211+
strcat(c->instruction, "&assignees=");
212+
strcat(c->instruction, assignees);
260213
}
261214

262-
static void set_labels(struct Prompt *p, char *labels) {
263-
assure_query_param_support(p);
215+
static void set_labels(struct Context *c, char *labels) {
216+
assure_query_param_support(c);
264217

265-
strcat(p->instruction, "&labels=");
266-
strcat(p->instruction, labels);
218+
strcat(c->instruction, "&labels=");
219+
strcat(c->instruction, labels);
267220
}
268221

269-
static void assure_query_param_support(struct Prompt *p) {
270-
if (strstr(p->instruction, "?expand=1") != NULL) return;
222+
static void assure_query_param_support(struct Context *c) {
223+
if (strstr(c->instruction, "?expand=1") != NULL) return;
271224

272-
strcat(p->instruction, "?expand=1");
225+
strcat(c->instruction, "?expand=1");
273226
}
274227

275-
static void warn_missing_branches(struct Prompt *p) {
276-
if (strstr(p->instruction, "...") != NULL) return;
228+
static void warn_missing_branches(struct Context *c) {
229+
if (strstr(c->instruction, "...") != NULL) return;
277230

278-
strcpy(p->warn, "WARNING: No branches have been selected. Execute `gh help` for more details.");
231+
strcpy(c->warn, "WARNING: No branches have been selected. Execute `gh help` for more details.");
279232
}

src/gh.h

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,21 @@
22
#include <unistd.h>
33
#include <stdlib.h>
44
#include <stdio.h>
5+
#include <prompt.h>
56

6-
#define MAX_STR_SIZE 512
7-
8-
#define INVALID_CMD -1
9-
#define MAX_CMD_OPTS 5
107
#define DEFAULT_CMD 1
118
#define REPO_CMD 1
129
#define HELP_CMD 2
1310
#define PULLS_CMD 3
1411
#define NEWPR_CMD 4
1512

16-
#define FIREFOX_INSTRUCTOR 1
17-
#define TERM_INSTRUCTOR 2
18-
19-
struct Option {
20-
char key[MAX_STR_SIZE];
21-
char value[MAX_STR_SIZE];
22-
};
23-
24-
struct Prompt {
25-
int cmd;
13+
struct Context {
2614
char instruction[MAX_STR_SIZE];
2715
char error[MAX_STR_SIZE];
2816
char warn[MAX_STR_SIZE];
29-
struct Option opts[MAX_CMD_OPTS];
17+
struct Prompt prompt;
3018
};
3119

32-
struct Prompt parse_prompt(int argc, char *argv[]);
33-
void add_instruction(struct Prompt *prompt);
20+
int cmd_table(char *cmd_str);
21+
void add_instruction(struct Context *context);
3422
FILE *find_git_config();

0 commit comments

Comments
 (0)