Skip to content

Commit 4e6bcdd

Browse files
committed
initial commit, general organization and likely broken atom implementation
0 parents  commit 4e6bcdd

File tree

8 files changed

+227
-0
lines changed

8 files changed

+227
-0
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Syndication (feed) generator library for golang.
2+
3+
Usage:
4+
5+
```golang
6+
7+
```
8+

atom.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package syndicate
2+
3+
import (
4+
"encoding/xml"
5+
"fmt"
6+
"net/url"
7+
"time"
8+
)
9+
10+
// Generates Atom feed as XML
11+
12+
const ns = "http://www.w3.org/2005/Atom"
13+
14+
type atomSummary struct {
15+
S string `xml:",chardata"`
16+
Type string `xml:"type,attr"`
17+
}
18+
19+
type atomEntry struct {
20+
XMLName xml.Name `xml:"entry"`
21+
Title string `xml:"title"`
22+
Link *atomLink
23+
Updated string `xml:"updated"`
24+
Id string `xml:"id"`
25+
Summary *atomSummary `xml:"summary"`
26+
}
27+
28+
type atomLink struct {
29+
XMLName xml.Name `xml:"link"`
30+
Href string `xml:"href,attr"`
31+
Rel string `xml:"rel,attr"`
32+
}
33+
34+
type atomFeed struct {
35+
XMLName xml.Name `xml:"feed"`
36+
Ns string `xml:"xmlns,attr"`
37+
Title string `xml:"title"`
38+
Link *atomLink
39+
Id string `xml:"id"`
40+
Updated string `xml:"updated"`
41+
Entries []*atomEntry
42+
}
43+
44+
type Atom struct {
45+
*Feed
46+
}
47+
48+
func newAtomEntry(i *Item) *atomEntry {
49+
id := i.Id
50+
// assume the description is html
51+
s := &atomSummary{i.Description, "html"}
52+
53+
// try to get a single timestamp, since we only have one in atom
54+
ts := i.Updated
55+
if ts.IsZero() {
56+
ts = i.Created
57+
}
58+
// <id>tag:blog.kowalczyk.info,2012-09-11:/item/1.html</id>
59+
if len(id) == 0 {
60+
// if there's no id set, try to create one, either from data or just a uuid
61+
if len(i.Link.Href) > 0 && (!i.Created.IsZero() || !i.Updated.IsZero()) {
62+
dateStr := ts.Format("2006-01-02")
63+
host, path := i.Link.Href, "/invalid.html"
64+
if url, err := url.Parse(i.Link.Href); err == nil {
65+
host, path = url.Host, url.Path
66+
}
67+
id = fmt.Sprintf("tag:%s,%s:%s", host, dateStr, path)
68+
} else {
69+
id = "urn:uuid:" + NewUUID().String()
70+
}
71+
}
72+
x := &atomEntry{
73+
Title: i.Title,
74+
Link: &atomLink{Href: i.Link.Href, Rel: i.Link.Rel},
75+
Summary: s,
76+
Id: id,
77+
Updated: i.Updated.Format(time.RFC3339)}
78+
return x
79+
}
80+
81+
func (a *Atom) FeedXml() interface{} {
82+
feed := &atomFeed{
83+
Ns: ns,
84+
Title: a.Title,
85+
Link: &atomLink{Href: a.Link.Href, Rel: a.Link.Rel},
86+
Id: a.Link.Href,
87+
Updated: a.Updated.Format(time.RFC3339)}
88+
for _, e := range a.Items {
89+
feed.Entries = append(feed.Entries, newAtomEntry(e))
90+
}
91+
92+
return feed
93+
}

doc.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
Create feed output in RSS or Atom.
3+
4+
*/
5+
package syndicate

feed.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package syndicate
2+
3+
import (
4+
"encoding/xml"
5+
"fmt"
6+
"time"
7+
)
8+
9+
type Link struct {
10+
Href string
11+
Rel string
12+
}
13+
14+
type Author struct {
15+
Name string
16+
Email string
17+
}
18+
19+
type Item struct {
20+
Title string
21+
Link *Link
22+
Author *Author
23+
Description string // used as description in rss, summary in atom
24+
Id string // used as guid in rss, id in atom
25+
Updated time.Time
26+
Created time.Time
27+
}
28+
29+
type Feed struct {
30+
Title string
31+
Link *Link
32+
Author *Author
33+
Created time.Time
34+
Updated time.Time
35+
Description string
36+
Subtitle string
37+
Id string
38+
Items []*Item
39+
}
40+
41+
func (f *Feed) Add(item *Item) {
42+
f.Items = append(f.Items, item)
43+
}
44+
45+
type XmlFeed interface {
46+
FeedXml() interface{}
47+
}
48+
49+
func ToXML(feed XmlFeed) (string, error) {
50+
x := feed.FeedXml()
51+
data, err := xml.MarshalIndent(x, " ", " ")
52+
if err != nil {
53+
return "", err
54+
}
55+
s := xml.Header[:len(xml.Header)-1] + string(data)
56+
return s, nil
57+
}
58+
59+
func (f *Feed) ToAtom() (string, error) {
60+
a := &Atom{f}
61+
return ToXML(a)
62+
}
63+
64+
func (f *Feed) ToRss(version ...float64) (string, error) {
65+
vers := 2.0
66+
if len(version) > 0 {
67+
vers = version[0]
68+
}
69+
/*
70+
r := &RssFeed{f}
71+
return ToXML(r)
72+
*/
73+
return fmt.Sprint(vers), nil
74+
}

feed_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package syndicate

rss.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package syndicate

uuid.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package syndicate
2+
3+
// relevant bits from https://github.com/abneptis/GoUUID/blob/master/uuid.go
4+
5+
import (
6+
"crypto/rand"
7+
"fmt"
8+
)
9+
10+
type UUID [16]byte
11+
12+
func NewUUID() *UUID {
13+
u := &UUID{}
14+
_, err := rand.Read(u[:16])
15+
if err != nil {
16+
panic(err)
17+
}
18+
19+
u[8] = (u[8] | 0x80) & 0xBf
20+
u[6] = (u[6] | 0x40) & 0x4f
21+
return u
22+
}
23+
24+
func (u *UUID) String() string {
25+
return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:])
26+
}

uuid_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package syndicate
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestUUID(t *testing.T) {
8+
s := NewUUID()
9+
s2 := NewUUID()
10+
if len(s) != 16 {
11+
t.Errorf("Expecting len of 16, got %d\n", len(s))
12+
}
13+
if len(s.String()) != 36 {
14+
t.Errorf("Expecting uuid hex string len of 36, got %d\n", len(s.String()))
15+
}
16+
if s == s2 {
17+
t.Errorf("Expecting different UUIDs to be different, but they are the same.\n")
18+
}
19+
}

0 commit comments

Comments
 (0)