Skip to content

Commit f572609

Browse files
committed
feat: parse XML for module-defined variables
1 parent ead4ac8 commit f572609

File tree

4 files changed

+144
-5
lines changed

4 files changed

+144
-5
lines changed

reference-converter/internal/output/output.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ type Directive struct {
2121
DescriptionHtml string `json:"description_html"`
2222
}
2323

24+
type Variable struct {
25+
Name string `json:"name"`
26+
DescriptionMd string `json:"description_md"`
27+
DescriptionHtml string `json:"description_html"`
28+
}
29+
2430
type Module struct {
2531
Id string `json:"id"`
2632
Name string `json:"name"`
2733
Directives []Directive `json:"directives"`
34+
Variables []Variable `json:"variables,omitempty"`
2835
}
2936

3037
func toModule(m *parse.Module) Module {
@@ -45,6 +52,13 @@ func toModule(m *parse.Module) Module {
4552
DescriptionHtml: directive.Prose.ToHTML(),
4653
})
4754
}
55+
for _, variable := range section.Variables {
56+
module.Variables = append(module.Variables, Variable{
57+
Name: variable.Name,
58+
DescriptionMd: variable.Prose.ToMarkdown(),
59+
DescriptionHtml: variable.Prose.ToHTML(),
60+
})
61+
}
4862
}
4963
return module
5064
}

reference-converter/internal/parse/elements.go

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package parse
33
import (
44
"encoding/xml"
55
"fmt"
6+
"regexp"
7+
"strings"
8+
69
"github.com/gomarkdown/markdown"
710
"github.com/gomarkdown/markdown/html"
811
"github.com/gomarkdown/markdown/parser"
9-
"regexp"
10-
"strings"
1112
)
1213

1314
// Syntax contain the markdown formatted syntax for the directive, very close to
@@ -153,10 +154,100 @@ type Directive struct {
153154
Prose Prose `xml:"para"`
154155
}
155156

157+
// Variable represents an NGINX variable defined by a module, e.g $binary_remote_addr.
158+
type Variable struct {
159+
Name string
160+
Prose Prose
161+
}
162+
163+
// unmarshalVariablesCML extracts NGINX variables from the common pattern:
164+
//
165+
// <section id="variables">
166+
// <list type="tag">
167+
// <tag-name><var>$VARIABLE_NAME</var></tag-name>
168+
// <tag-desc>$DOCUMENTATION</tag-desc>
169+
// <tag-name><var>$VARIABLE_NAME</var><value>$DYNAMIC_SUFFIX</value></tag-name>
170+
// <tag-desc>$DOCUMENTATION</tag-desc>
171+
// </list>
172+
// </section>
173+
func unmarshalVariablesCML(d *xml.Decoder, start xml.StartElement) ([]Variable, error) {
174+
var v struct {
175+
ID string `xml:"id,attr"`
176+
Paragraphs []struct {
177+
List struct {
178+
TagNames []struct {
179+
Name string `xml:"var"`
180+
Suffix string `xml:"value"`
181+
} `xml:"tag-name"`
182+
TagDesc []Prose `xml:"tag-desc"`
183+
} `xml:"list"`
184+
} `xml:"para"`
185+
}
186+
if err := d.DecodeElement(&v, &start); err != nil {
187+
return nil, fmt.Errorf("failed to parse variables: %w", err)
188+
}
189+
var vs []Variable
190+
for _, para := range v.Paragraphs {
191+
if len(para.List.TagDesc) != len(para.List.TagNames) {
192+
return nil, fmt.Errorf(
193+
"invalid variables section, need to have the same number of names (%d) and descriptions (%d)",
194+
len(para.List.TagNames), len(para.List.TagDesc),
195+
)
196+
}
197+
for idx, tn := range para.List.TagNames {
198+
name := tn.Name
199+
if tn.Suffix != "" {
200+
name += strings.ToUpper(tn.Suffix)
201+
}
202+
vs = append(vs, Variable{
203+
Name: name,
204+
Prose: para.List.TagDesc[idx],
205+
})
206+
}
207+
}
208+
209+
return vs, nil
210+
}
211+
156212
type Section struct {
157-
ID string `xml:"id,attr"`
158-
Directives []Directive `xml:"directive"`
159-
Prose Prose `xml:"para"`
213+
ID string
214+
Directives []Directive
215+
Prose Prose
216+
Variables []Variable
217+
}
218+
219+
// UnmarshalXML processes the elements in-order to generate correct content
220+
func (p *Section) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
221+
attrs := newAttrs(start.Attr)
222+
if attrs["id"] == "variables" {
223+
// parse as a list of variables
224+
vs, err := unmarshalVariablesCML(d, start)
225+
if err != nil {
226+
return fmt.Errorf("failed to unmarshall variables: %w", err)
227+
}
228+
*p = Section{
229+
ID: "variables",
230+
Variables: vs,
231+
}
232+
return nil
233+
}
234+
235+
// parse as a normal section
236+
var sec struct {
237+
ID string `xml:"id,attr"`
238+
Directives []Directive `xml:"directive"`
239+
Prose Prose `xml:"para"`
240+
}
241+
if err := d.DecodeElement(&sec, &start); err != nil {
242+
return err
243+
}
244+
245+
*p = Section{
246+
ID: sec.ID,
247+
Directives: sec.Directives,
248+
Prose: sec.Prose,
249+
}
250+
return nil
160251
}
161252

162253
type Module struct {

reference-converter/internal/parse/parse_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@ func TestParse(t *testing.T) {
4141
},
4242
},
4343
},
44+
{
45+
ID: "variables",
46+
Variables: []parse.Variable{
47+
{
48+
Name: "$wildcard_var_NAME",
49+
Prose: parse.Prose{
50+
{Content: "\nI support a dynamic suffix *`name`*\n"},
51+
},
52+
},
53+
{
54+
Name: "$variable",
55+
Prose: parse.Prose{
56+
{Content: "\nI am a variable with `formatting` in my desc\n"},
57+
},
58+
},
59+
},
60+
},
4461
},
4562
},
4663
},

reference-converter/internal/parse/testdata/module.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,21 @@ Can have more than one, with some&nbsp;html&mdash;ish entities and <literal>verb
2727

2828
</directive>
2929
</section>
30+
<section id="variables">
31+
<para>We add these variables:</para>
32+
33+
<para>
34+
<list type="tag">
35+
<tag-name><var>$wildcard_var_</var><value>name</value></tag-name>
36+
<tag-desc>
37+
I support a dynamic suffix <value>name</value>
38+
</tag-desc>
39+
40+
<tag-name><var>$variable</var></tag-name>
41+
<tag-desc>
42+
I am a variable with <literal>formatting</literal> in my desc
43+
</tag-desc>
44+
</list>
45+
</para>
46+
</section>
3047
</module>

0 commit comments

Comments
 (0)