Skip to content

Commit c2db1e2

Browse files
authored
Merge pull request #2 from zweihander/feat/href-export
Feat/href export
2 parents cd83fa8 + 5211de2 commit c2db1e2

File tree

12 files changed

+227
-20737
lines changed

12 files changed

+227
-20737
lines changed

_schema/121.json

Lines changed: 1 addition & 20703 deletions
Large diffs are not rendered by default.

constructor.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import (
99

1010
// Constructor represents constructor documentation.
1111
type Constructor struct {
12-
Name string `json:"name"`
13-
Description []string `json:"description,omitempty"`
14-
Fields map[string]string `json:"fields,omitempty"`
12+
Name string `json:"name"`
13+
Description []string `json:"description,omitempty"`
14+
Links []string `json:"links,omitempty"`
15+
Fields map[string]ParamDescription `json:"fields,omitempty"`
1516
}
1617

1718
// ParseConstructor parses html documentation from reader and produces Constructor.
@@ -20,9 +21,12 @@ func ParseConstructor(reader io.Reader) (*Constructor, error) {
2021
if err != nil {
2122
return nil, errors.Errorf("failed to parse document: %w", err)
2223
}
24+
25+
desc, links := docDescription(doc)
2326
return &Constructor{
2427
Name: docTitle(doc),
25-
Description: docDescription(doc),
28+
Description: desc,
29+
Links: links,
2630
Fields: docParams(doc),
2731
}, nil
2832
}

constructor_test.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,28 @@ func TestConstructor(t *testing.T) {
2323
expected := &Constructor{
2424
Name: "userProfilePhoto",
2525
Description: []string{"User profile photo."},
26-
Fields: map[string]string{
27-
"dc_id": "DC ID where the photo is stored",
28-
"flags": "Flags, see TL conditional fields",
29-
"has_video": "Whether an animated profile picture is available for this user",
30-
"photo_big": "Location of the file, corresponding to the big profile photo thumbnail",
31-
"photo_id": "Identifier of the respective photoParameter added in Layer 2",
32-
"photo_small": "Location of the file, corresponding to the small profile photo thumbnail",
26+
Fields: map[string]ParamDescription{
27+
"dc_id": {
28+
Description: "DC ID where the photo is stored",
29+
},
30+
"flags": {
31+
Description: "Flags, see TL conditional fields¹",
32+
Links: []string{"https://core.telegram.org/mtproto/TL-combinators#conditional-fields"},
33+
},
34+
"has_video": {
35+
Description: "Whether an animated profile picture¹ is available for this user",
36+
Links: []string{"https://core.telegram.org/api/files#animated-profile-pictures"},
37+
},
38+
"photo_big": {
39+
Description: "Location of the file, corresponding to the big profile photo thumbnail",
40+
},
41+
"photo_id": {
42+
Description: "Identifier of the respective photoParameter added in Layer 2¹",
43+
Links: []string{"https://core.telegram.org/api/layers#layer-2"},
44+
},
45+
"photo_small": {
46+
Description: "Location of the file, corresponding to the small profile photo thumbnail",
47+
},
3348
},
3449
}
3550
require.Equal(t, expected, v)

getdoc.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"strings"
77

88
"github.com/PuerkitoBio/goquery"
9+
10+
"github.com/gotd/getdoc/href"
911
)
1012

1113
// Doc represents full documentation description.
@@ -23,25 +25,27 @@ func docTitle(doc *goquery.Document) string {
2325
}
2426

2527
// docDescription extracts description lines from document.
26-
func docDescription(doc *goquery.Document) []string {
27-
var description []string
28+
func docDescription(doc *goquery.Document) (desc, links []string) {
2829
doc.Find("#dev_page_content").Each(func(i int, s *goquery.Selection) {
2930
s.Children().EachWithBreak(func(i int, selection *goquery.Selection) bool {
3031
if selection.Is("p") && selection.Text() != "" {
31-
// Trimming space and handling newlines.
32+
hrefs := href.Replace(selection)
33+
3234
text := strings.TrimSpace(selection.Text())
3335
for _, part := range strings.Split(text, "\n") {
3436
part = strings.TrimSpace(part)
3537
if part == "" {
3638
continue
3739
}
38-
description = append(description, part)
40+
desc = append(desc, part)
3941
}
42+
43+
links = append(links, addHost(hrefs)...)
4044
}
4145
return !selection.HasClass("clearfix")
4246
})
4347
})
44-
return description
48+
return
4549
}
4650

4751
// docTableAfter extracts table after selector "after".
@@ -69,19 +73,30 @@ func docTableAfter(doc *goquery.Document, after string) *goquery.Selection {
6973
return table.First().Find("tbody > tr")
7074
}
7175

76+
type ParamDescription struct {
77+
Description string `json:"description"`
78+
Links []string `json:"links"`
79+
}
80+
7281
// docParams extract parameters documentation from document.
7382
//
74-
// Key is parameter name, value is documentation string.
75-
func docParams(doc *goquery.Document) map[string]string {
76-
fields := make(map[string]string)
83+
// Key is parameter name, value is documentation struct.
84+
func docParams(doc *goquery.Document) map[string]ParamDescription {
85+
fields := make(map[string]ParamDescription)
86+
7787
docTableAfter(doc, "#parameters").
7888
Each(func(i int, row *goquery.Selection) {
7989
var rowContents []string
90+
var links []string
8091
row.Find("td").Each(func(i int, column *goquery.Selection) {
81-
rowContents = append(rowContents, strings.TrimSpace(column.Text()))
92+
links = addHost(href.Replace(column))
93+
rowContents = append(rowContents, column.Text())
8294
})
8395
if len(rowContents) == 3 {
84-
fields[rowContents[0]] = rowContents[2]
96+
fields[rowContents[0]] = ParamDescription{
97+
Description: rowContents[2],
98+
Links: links,
99+
}
85100
}
86101
})
87102
return fields

href/href_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package href
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/PuerkitoBio/goquery"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestHref(t *testing.T) {
12+
tests := []struct {
13+
Input string
14+
Text string
15+
HREFs []string
16+
}{
17+
{
18+
Input: `<p>Hello! <a href="https://foo.com/bar">Click me!</a></p>`,
19+
Text: `Hello! Click me!¹`,
20+
HREFs: []string{"https://foo.com/bar"},
21+
},
22+
{
23+
Input: `<p>Hello! <a href="/foo">Click me</a> again!</p>`,
24+
Text: `Hello! Click me¹ again!`,
25+
HREFs: []string{"/foo"},
26+
},
27+
}
28+
29+
for _, test := range tests {
30+
doc, err := goquery.NewDocumentFromReader(strings.NewReader(test.Input))
31+
assert.NoError(t, err)
32+
hrefs := Replace(doc.Selection)
33+
assert.Equal(t, test.Text, doc.Text())
34+
assert.Equal(t, hrefs, test.HREFs)
35+
}
36+
}

href/replace.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package href
2+
3+
import (
4+
"unicode"
5+
6+
"github.com/PuerkitoBio/goquery"
7+
)
8+
9+
// Replace replaces all found HREFs with [index] symbol at end of node text.
10+
// It returns slice of replaced HREFs.
11+
func Replace(selection *goquery.Selection) (hrefs []string) {
12+
replaceHrefsRecursively(selection, map[int]struct{}{}, &hrefs)
13+
return hrefs
14+
}
15+
16+
func replaceHrefsRecursively(selection *goquery.Selection, accum map[int]struct{}, hrefs *[]string) {
17+
if _, processed := accum[selection.Index()]; processed {
18+
return
19+
}
20+
21+
if path, ok := selection.Attr("href"); ok {
22+
accum[selection.Index()] = struct{}{}
23+
24+
*hrefs = append(*hrefs, path)
25+
26+
text, cut := cutRightSpaces(selection.Text())
27+
text += superscript(len(*hrefs))
28+
text += cut
29+
30+
selection.SetText(text)
31+
}
32+
33+
selection.Find("*").Each(func(i int, s *goquery.Selection) {
34+
replaceHrefsRecursively(s, accum, hrefs)
35+
})
36+
}
37+
38+
func cutRightSpaces(input string) (result, cut string) {
39+
var (
40+
r = []rune(input)
41+
c []rune
42+
)
43+
44+
for i := len(r) - 1; i >= 0; i-- {
45+
if unicode.IsSpace(r[i]) {
46+
c = append(c, r[i])
47+
r = r[:i]
48+
} else {
49+
break
50+
}
51+
}
52+
53+
return string(r), string(c)
54+
}

href/superscript.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package href
2+
3+
import "strconv"
4+
5+
func superscript(i int) string {
6+
var out []rune
7+
for _, r := range strconv.Itoa(i) {
8+
var superscript rune
9+
switch r {
10+
case '0':
11+
superscript = '⁰'
12+
case '1':
13+
superscript = '¹'
14+
case '2':
15+
superscript = '²'
16+
case '3':
17+
superscript = '³'
18+
case '4':
19+
superscript = '⁴'
20+
case '5':
21+
superscript = '⁵'
22+
case '6':
23+
superscript = '⁶'
24+
case '7':
25+
superscript = '⁷'
26+
case '8':
27+
superscript = '⁸'
28+
case '9':
29+
superscript = '⁹'
30+
default:
31+
panic(r)
32+
}
33+
34+
out = append(out, superscript)
35+
}
36+
37+
return string(out)
38+
}

internal/bindata.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

links.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package getdoc
2+
3+
import (
4+
"net/url"
5+
)
6+
7+
func addHost(hrefs []string) (s []string) {
8+
for _, href := range hrefs {
9+
u, err := url.Parse(href)
10+
if err != nil {
11+
panic(err)
12+
}
13+
14+
if u.Host == "" {
15+
href = "https://core.telegram.org" + href
16+
}
17+
18+
s = append(s, href)
19+
}
20+
21+
return
22+
}

method.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import (
1010

1111
// Method represents method documentation.
1212
type Method struct {
13-
Name string `json:"name"`
14-
Description []string `json:"description,omitempty"`
15-
Parameters map[string]string `json:"parameters,omitempty"`
16-
Errors []Error `json:"errors,omitempty"`
13+
Name string `json:"name"`
14+
Description []string `json:"description,omitempty"`
15+
Links []string `json:"links,omitempty"`
16+
Parameters map[string]ParamDescription `json:"parameters,omitempty"`
17+
Errors []Error `json:"errors,omitempty"`
1718
}
1819

1920
// Error represent possible error documentation.
@@ -55,9 +56,12 @@ func ParseMethod(reader io.Reader) (*Method, error) {
5556
if err != nil {
5657
return nil, errors.Errorf("failed to parse document: %w", err)
5758
}
59+
60+
desc, links := docDescription(doc)
5861
return &Method{
5962
Name: docTitle(doc),
60-
Description: docDescription(doc),
63+
Description: desc,
64+
Links: links,
6165
Parameters: docParams(doc),
6266
Errors: docErrors(doc),
6367
}, nil

method_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ func TestParseMethod(t *testing.T) {
2323
expected := &Method{
2424
Name: "langpack.getDifference",
2525
Description: []string{"Get new strings in languagepack"},
26-
Parameters: map[string]string{
27-
"from_version": "Previous localization pack version",
28-
"lang_code": "Language code",
29-
"lang_pack": "Language pack",
26+
Parameters: map[string]ParamDescription{
27+
"from_version": {Description: "Previous localization pack version"},
28+
"lang_code": {Description: "Language code"},
29+
"lang_pack": {Description: "Language pack"},
3030
},
3131
Errors: []Error{
3232
{Code: 400, Type: "LANG_PACK_INVALID", Description: "The provided language pack is invalid"},

type.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import (
1010
// Type represents type (aka class) documentation.
1111
type Type struct {
1212
Name string `json:"name"`
13-
Description []string `json:"description"`
13+
Description []string `json:"description,omitempty"`
14+
Links []string `json:"links,omitempty"`
1415
}
1516

1617
// ParseType parses Type documentation from reader.
@@ -19,8 +20,11 @@ func ParseType(reader io.Reader) (*Type, error) {
1920
if err != nil {
2021
return nil, errors.Errorf("failed to parse document: %w", err)
2122
}
23+
24+
desc, links := docDescription(doc)
2225
return &Type{
2326
Name: docTitle(doc),
24-
Description: docDescription(doc),
27+
Description: desc,
28+
Links: links,
2529
}, nil
2630
}

0 commit comments

Comments
 (0)