reorganizing server-related packages

This commit is contained in:
Nazar Kanaev
2021-04-01 00:24:18 +01:00
parent b04e8c1e93
commit 528df7fb4a
12 changed files with 4 additions and 4 deletions

70
src/server/opml/opml.go Normal file
View File

@@ -0,0 +1,70 @@
package opml
import (
"fmt"
"html"
"strings"
)
type Folder struct {
Title string
Folders []Folder
Feeds []Feed
}
type Feed struct {
Title string
FeedUrl string
SiteUrl string
}
func (f Folder) AllFeeds() []Feed {
feeds := make([]Feed, 0)
feeds = append(feeds, f.Feeds...)
for _, subfolder := range f.Folders {
feeds = append(feeds, subfolder.AllFeeds()...)
}
return feeds
}
var e = html.EscapeString
var indent = " "
var nl = "\n"
func (f Folder) outline(level int) string {
builder := strings.Builder{}
prefix := strings.Repeat(indent, level)
if level > 0 {
builder.WriteString(prefix + fmt.Sprintf(`<outline text="%s">`+nl, e(f.Title)))
}
for _, folder := range f.Folders {
builder.WriteString(folder.outline(level + 1))
}
for _, feed := range f.Feeds {
builder.WriteString(feed.outline(level + 1))
}
if level > 0 {
builder.WriteString(prefix + `</outline>` + nl)
}
return builder.String()
}
func (f Feed) outline(level int) string {
return strings.Repeat(indent, level) + fmt.Sprintf(
`<outline type="rss" text="%s" xmlUrl="%s" htmlUrl="%s"/>`+nl,
e(f.Title), e(f.FeedUrl), e(f.SiteUrl),
)
}
func (f Folder) OPML() string {
builder := strings.Builder{}
builder.WriteString(`<?xml version="1.0" encoding="UTF-8"?>` + nl)
builder.WriteString(`<opml version="1.1">` + nl)
builder.WriteString(`<head><title>subscriptions</title></head>` + nl)
builder.WriteString(`<body>` + nl)
builder.WriteString(f.outline(0))
builder.WriteString(`</body>` + nl)
builder.WriteString(`</opml>` + nl)
return builder.String()
}

View File

@@ -0,0 +1,54 @@
package opml
import (
"reflect"
"testing"
)
func TestOPML(t *testing.T) {
have := (Folder{
Title: "",
Feeds: []Feed{
{
Title: "title1",
FeedUrl: "https://baz.com/feed.xml",
SiteUrl: "https://baz.com/",
},
},
Folders: []Folder{
{
Title: "sub",
Feeds: []Feed{
{
Title: "subtitle1",
FeedUrl: "https://foo.com/feed.xml",
SiteUrl: "https://foo.com/",
},
{
Title: "&>",
FeedUrl: "https://bar.com/feed.xml",
SiteUrl: "https://bar.com/",
},
},
Folders: []Folder{},
},
},
}).OPML()
want := `<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.1">
<head><title>subscriptions</title></head>
<body>
<outline text="sub">
<outline type="rss" text="subtitle1" xmlUrl="https://foo.com/feed.xml" htmlUrl="https://foo.com/"/>
<outline type="rss" text="&amp;&gt;" xmlUrl="https://bar.com/feed.xml" htmlUrl="https://bar.com/"/>
</outline>
<outline type="rss" text="title1" xmlUrl="https://baz.com/feed.xml" htmlUrl="https://baz.com/"/>
</body>
</opml>
`
if !reflect.DeepEqual(want, have) {
t.Logf("want: %s", want)
t.Logf("have: %s", have)
t.Fatal("invalid opml")
}
}

49
src/server/opml/read.go Normal file
View File

@@ -0,0 +1,49 @@
package opml
import (
"encoding/xml"
"io"
)
type opml struct {
XMLName xml.Name `xml:"opml"`
Outlines []outline `xml:"body>outline"`
}
type outline struct {
Type string `xml:"type,attr,omitempty"`
Title string `xml:"text,attr"`
FeedUrl string `xml:"xmlUrl,attr,omitempty"`
SiteUrl string `xml:"htmlUrl,attr,omitempty"`
Outlines []outline `xml:"outline,omitempty"`
}
func buildFolder(title string, outlines []outline) Folder {
folder := Folder{Title: title}
for _, outline := range outlines {
if outline.Type == "rss" {
folder.Feeds = append(folder.Feeds, Feed{
Title: outline.Title,
FeedUrl: outline.FeedUrl,
SiteUrl: outline.SiteUrl,
})
} else {
subfolder := buildFolder(outline.Title, outline.Outlines)
folder.Folders = append(folder.Folders, subfolder)
}
}
return folder
}
func Parse(r io.Reader) (Folder, error) {
val := new(opml)
decoder := xml.NewDecoder(r)
decoder.Entity = xml.HTMLEntity
decoder.Strict = false
err := decoder.Decode(&val)
if err != nil {
return Folder{}, err
}
return buildFolder("", val.Outlines), nil
}

View File

@@ -0,0 +1,58 @@
package opml
import (
"reflect"
"strings"
"testing"
)
func TestParse(t *testing.T) {
have, _ := Parse(strings.NewReader(`
<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.1">
<head><title>Subscriptions</title></head>
<body>
<outline text="sub">
<outline type="rss" text="subtitle1" description="sub1"
xmlUrl="https://foo.com/feed.xml" htmlUrl="https://foo.com/"/>
<outline type="rss" text="&amp;&gt;" description="&lt;&gt;"
xmlUrl="https://bar.com/feed.xml" htmlUrl="https://bar.com/"/>
</outline>
<outline type="rss" text="title1" description="desc1"
xmlUrl="https://baz.com/feed.xml" htmlUrl="https://baz.com/"/>
</body>
</opml>
`))
want := Folder{
Title: "",
Feeds: []Feed{
{
Title: "title1",
FeedUrl: "https://baz.com/feed.xml",
SiteUrl: "https://baz.com/",
},
},
Folders: []Folder{
{
Title: "sub",
Feeds: []Feed{
{
Title: "subtitle1",
FeedUrl: "https://foo.com/feed.xml",
SiteUrl: "https://foo.com/",
},
{
Title: "&>",
FeedUrl: "https://bar.com/feed.xml",
SiteUrl: "https://bar.com/",
},
},
},
},
}
if !reflect.DeepEqual(want, have) {
t.Logf("want: %#v", want)
t.Logf("have: %#v", have)
t.Fatal("invalid opml")
}
}