From c62fbcdc507f62d81189874b017859c2f269e4b0 Mon Sep 17 00:00:00 2001 From: Amund Tenstad Date: Thu, 29 Feb 2024 00:30:20 +0100 Subject: [PATCH] fix: escape asterix in adoc (#64) --- renderer/asciidoctor.go | 23 +++++++++++++++++++++++ renderer/asciidoctor_test.go | 15 +++++++++++++++ renderer/markdown.go | 14 +++++++------- templates/asciidoctor/type.tpl | 2 +- test/api/v1/guestbook_types.go | 3 ++- test/expected.asciidoc | 3 ++- test/expected.md | 4 ++-- 7 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 renderer/asciidoctor_test.go diff --git a/renderer/asciidoctor.go b/renderer/asciidoctor.go index c7ec5dd..be144d5 100644 --- a/renderer/asciidoctor.go +++ b/renderer/asciidoctor.go @@ -84,6 +84,7 @@ func (adr *AsciidoctorRenderer) ToFuncMap() template.FuncMap { "ShouldRenderType": adr.ShouldRenderType, "TypeID": adr.TypeID, "RenderFieldDoc": adr.RenderFieldDoc, + "RenderValidation": adr.RenderValidation, } } @@ -157,3 +158,25 @@ func (adr *AsciidoctorRenderer) RenderFieldDoc(text string) string { // See: https://docs.asciidoctor.org/asciidoc/latest/blocks/hard-line-breaks return strings.Join(lines, " +\n\n") } + +func (adr *AsciidoctorRenderer) RenderValidation(text string) string { + return escapeFirstAsterixInEachPair(text) +} + +// escapeFirstAsterixInEachPair escapes the first asterix in each pair of +// asterixes in text. E.g. "*a*b*c*" -> "\*a*b\*c*" and "*a*b*" -> "\*a*b*". +func escapeFirstAsterixInEachPair(text string) string { + index := -1 + for i := 0; i < len(text); i++ { + if text[i] == '*' { + if index >= 0 { + text = text[:index] + "\\" + text[index:] + index = -1 + i++ + } else { + index = i + } + } + } + return text +} diff --git a/renderer/asciidoctor_test.go b/renderer/asciidoctor_test.go new file mode 100644 index 0000000..3d67c36 --- /dev/null +++ b/renderer/asciidoctor_test.go @@ -0,0 +1,15 @@ +package renderer + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_escapeFirstAsterixInEachPair(t *testing.T) { + assert.Equal(t, "[a-z]", escapeFirstAsterixInEachPair("[a-z]")) + assert.Equal(t, "[a-z]*", escapeFirstAsterixInEachPair("[a-z]*")) + assert.Equal(t, `0\*[a-z]*`, escapeFirstAsterixInEachPair(`0*[a-z]*`)) + assert.Equal(t, `0\*[a-z]*[a-z]*[0-9]`, escapeFirstAsterixInEachPair(`0*[a-z]*[a-z]*[0-9]`)) + assert.Equal(t, `0\*[a-z]*[a-z]\*[0-9]*`, escapeFirstAsterixInEachPair(`0*[a-z]*[a-z]*[0-9]*`)) +} diff --git a/renderer/markdown.go b/renderer/markdown.go index 34b2f88..fd502fd 100644 --- a/renderer/markdown.go +++ b/renderer/markdown.go @@ -139,13 +139,6 @@ func (m *MarkdownRenderer) RenderExternalLink(link, text string) string { return fmt.Sprintf("[%s](%s)", text, link) } -func (m *MarkdownRenderer) RenderDefault(text string) string { - return strings.NewReplacer( - "{", "\\{", - "}", "\\}", - ).Replace(text) -} - func (m *MarkdownRenderer) RenderGVLink(gv types.GroupVersionDetails) string { return m.RenderLocalLink(gv.GroupVersionString()) } @@ -158,3 +151,10 @@ func (m *MarkdownRenderer) RenderFieldDoc(text string) string { // Replace newlines with 2 line breaks so that they don't break the Markdown table formatting. return strings.ReplaceAll(out, "\n", "

") } + +func (m *MarkdownRenderer) RenderDefault(text string) string { + return strings.NewReplacer( + "{", "\\{", + "}", "\\}", + ).Replace(text) +} diff --git a/templates/asciidoctor/type.tpl b/templates/asciidoctor/type.tpl index 7b72b29..b28b51a 100644 --- a/templates/asciidoctor/type.tpl +++ b/templates/asciidoctor/type.tpl @@ -35,7 +35,7 @@ {{ end -}} {{ range $type.Members -}} -| *`{{ .Name }}`* __{{ asciidocRenderType .Type }}__ | {{ template "type_members" . }} | {{ .Default }} | {{ range .Validation -}} {{ . }} + +| *`{{ .Name }}`* __{{ asciidocRenderType .Type }}__ | {{ template "type_members" . }} | {{ .Default }} | {{ range .Validation -}} {{ asciidocRenderValidation . }} + {{ end }} {{ end -}} |=== diff --git a/test/api/v1/guestbook_types.go b/test/api/v1/guestbook_types.go index c142576..479b2df 100644 --- a/test/api/v1/guestbook_types.go +++ b/test/api/v1/guestbook_types.go @@ -103,13 +103,14 @@ type PositiveInt int type GuestbookEntry struct { // Name of the guest (pipe | should be escaped) // +kubebuilder:validation:MaxLength=80 + // +kubebuilder:validation:Pattern=`0*[a-z0-9]*[a-z]*[0-9]` Name string `json:"name,omitempty"` // Time of entry Time metav1.Time `json:"time,omitempty"` // Comment by guest. This can be a multi-line comment. // // Just like this one. - // +kubebuilder:validation:Pattern=`[a-z0-9]` + // +kubebuilder:validation:Pattern=`0*[a-z0-9]*[a-z]*[0-9]*` Comment string `json:"comment,omitempty"` // Rating provided by the guest Rating Rating `json:"rating,omitempty"` diff --git a/test/expected.asciidoc b/test/expected.asciidoc index d367b2e..cd7cc09 100644 --- a/test/expected.asciidoc +++ b/test/expected.asciidoc @@ -116,11 +116,12 @@ GuestbookEntry defines an entry in a guest book. |=== | Field | Description | Default | Validation | *`name`* __string__ | Name of the guest (pipe \| should be escaped) | | MaxLength: 80 + +Pattern: `0\*[a-z0-9]*[a-z]*[0-9]` + | *`time`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta[$$Time$$]__ | Time of entry | | | *`comment`* __string__ | Comment by guest. This can be a multi-line comment. + -Just like this one. | | Pattern: `[a-z0-9]` + +Just like this one. | | Pattern: `0\*[a-z0-9]*[a-z]\*[0-9]*` + | *`rating`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-rating[$$Rating$$]__ | Rating provided by the guest | | Maximum: 5 + Minimum: 1 + diff --git a/test/expected.md b/test/expected.md index 2b077f7..0917df7 100644 --- a/test/expected.md +++ b/test/expected.md @@ -91,9 +91,9 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `name` _string_ | Name of the guest (pipe \| should be escaped) | | MaxLength: 80
| +| `name` _string_ | Name of the guest (pipe \| should be escaped) | | MaxLength: 80
Pattern: `0*[a-z0-9]*[a-z]*[0-9]`
| | `time` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta)_ | Time of entry | | | -| `comment` _string_ | Comment by guest. This can be a multi-line comment.

Just like this one. | | Pattern: `[a-z0-9]`
| +| `comment` _string_ | Comment by guest. This can be a multi-line comment.

Just like this one. | | Pattern: `0*[a-z0-9]*[a-z]*[0-9]*`
| | `rating` _[Rating](#rating)_ | Rating provided by the guest | | Maximum: 5
Minimum: 1
|