v / cmd / tools / modules / vbugreport / report.v
174 lines · 159 sloc · 4.66 KB · 4ab6d9031022fbcfd59a621ee64600abe13053dd
Raw
1module vbugreport
2
3import net.urllib
4import os
5
6const github_bug_issue_form_base_uri = 'https://github.com/vlang/v/issues/new?template=bug-report.yml'
7const github_issue_form_url_soft_limit = 7500
8
9pub struct BugReport {
10 description string
11 reproduction string
12 expected string
13 current string
14 solution string
15 context string
16 version string
17 environment string
18}
19
20struct IssueFormField {
21 name string
22 label string
23 value string
24}
25
26pub enum BugReportDeliveryMode {
27 github_form
28 local_report
29}
30
31pub struct BugReportDelivery {
32pub:
33 mode BugReportDeliveryMode
34 uri string
35 local_report_path string
36 local_report_body string
37}
38
39fn field_value_or_default(value string, default_value string) string {
40 trimmed := value.trim_space()
41 if trimmed.len == 0 {
42 return default_value
43 }
44 return trimmed
45}
46
47fn fenced_block(language string, value string) string {
48 content := field_value_or_default(value, 'N/A')
49 return '```' + language + '\n' + content + '\n```'
50}
51
52fn quoted_command(file_path string, generated_file string, user_args string) string {
53 mut compile_cmd := './vdbg'
54 if user_args.len > 0 {
55 compile_cmd += ' ${user_args}'
56 }
57 compile_cmd += ' ${os.quoted_path(file_path)}'
58 return './v -g -o vdbg cmd/v && ${compile_cmd} && ${os.quoted_path(os.real_path(generated_file))}'
59}
60
61// new_bug_report builds the structured fields used by the GitHub bug report form.
62pub fn new_bug_report(file_path string, generated_file string, user_args string, expected_result string, version string, vdoctor_output string, file_content string, build_output string) BugReport {
63 command := quoted_command(file_path, generated_file, user_args)
64 source_name := os.file_name(file_path)
65 return BugReport{
66 description: 'Running `${command}` on `${source_name}` produced a compiler error.'
67 reproduction: '${fenced_block('sh', command)}\n\n${fenced_block('v', file_content)}'
68 expected: field_value_or_default(expected_result, 'N/A')
69 current: fenced_block('', build_output)
70 solution: '_No response_'
71 context: 'Generated by `v bug` from `${file_path}`.'
72 version: field_value_or_default(version, 'N/A')
73 environment: fenced_block('', vdoctor_output)
74 }
75}
76
77// issue_form_fields must stay aligned with `.github/ISSUE_TEMPLATE/bug-report.yml`.
78fn issue_form_fields(report BugReport) []IssueFormField {
79 return [
80 IssueFormField{
81 name: 'description'
82 label: 'Describe the bug'
83 value: report.description
84 },
85 IssueFormField{
86 name: 'reproduction'
87 label: 'Reproduction Steps'
88 value: report.reproduction
89 },
90 IssueFormField{
91 name: 'expected'
92 label: 'Expected Behavior'
93 value: report.expected
94 },
95 IssueFormField{
96 name: 'current'
97 label: 'Current Behavior'
98 value: report.current
99 },
100 IssueFormField{
101 name: 'solution'
102 label: 'Possible Solution'
103 value: report.solution
104 },
105 IssueFormField{
106 name: 'context'
107 label: 'Additional Information/Context'
108 value: report.context
109 },
110 IssueFormField{
111 name: 'version'
112 label: 'V version'
113 value: report.version
114 },
115 IssueFormField{
116 name: 'environment'
117 label: 'Environment details (OS name and version, etc.)'
118 value: report.environment
119 },
120 ]
121}
122
123fn github_issue_form_uri(report BugReport) string {
124 mut query_params := []string{}
125 for field in issue_form_fields(report) {
126 query_params << '${field.name}=${urllib.query_escape(field.value)}'
127 }
128 return '${github_bug_issue_form_base_uri}&${query_params.join('&')}'
129}
130
131fn bug_report_markdown(report BugReport) string {
132 mut lines := [
133 '# V Bug Report',
134 '',
135 'Generated by `v bug`.',
136 '',
137 ]
138 for field in issue_form_fields(report) {
139 lines << '### ${field.label}'
140 lines << ''
141 lines << field.value
142 lines << ''
143 }
144 return lines.join('\n')
145}
146
147fn bug_report_file_path(file_path string) string {
148 base_name := os.file_name(file_path)
149 stem := if base_name.contains('.') { base_name.all_before_last('.') } else { base_name }
150 file_name := if stem.len == 0 { 'v-bug-report.md' } else { '${stem}.bug-report.md' }
151 file_dir := os.dir(file_path)
152 return if file_dir.len == 0 || file_dir == '.' {
153 file_name
154 } else {
155 os.join_path(file_dir, file_name)
156 }
157}
158
159// prepare_bug_report_delivery chooses between a prefilled issue-form URL and a local markdown fallback.
160pub fn prepare_bug_report_delivery(report BugReport, file_path string) BugReportDelivery {
161 uri := github_issue_form_uri(report)
162 if uri.len <= github_issue_form_url_soft_limit {
163 return BugReportDelivery{
164 mode: .github_form
165 uri: uri
166 }
167 }
168 return BugReportDelivery{
169 mode: .local_report
170 uri: github_bug_issue_form_base_uri
171 local_report_path: bug_report_file_path(file_path)
172 local_report_body: bug_report_markdown(report)
173 }
174}
175