M barefeed.go => barefeed.go +2 -29
@@ 6,27 6,15 @@ package barefeed
import (
"bytes"
"io"
- "io/ioutil"
+ "os"
"path/filepath"
"git.sr.ht/~sircmpwn/go-bare"
)
-// Message it the root type of a barefeed. It allows for future versions of the
-// spec and allowing backward compatability
-type Message interface {
- bare.Union
-}
-
-func init() {
- bare.RegisterUnion((*Message)(nil)).
- Member(*new(MessageV1), 0)
-
-}
-
// FromFile will read barefeed from a file
func FromFile(path string) (*Message, error) {
- b, err := ioutil.ReadFile(filepath.Clean(path))
+ b, err := os.ReadFile(filepath.Clean(path))
if err != nil {
return nil, err
}
@@ 50,18 38,3 @@ func FromBytes(b []byte) (*Message, error) {
}
return &m, nil
}
-
-// Bytes will return the barefeed as a slice of bytes
-func Bytes(t Message) ([]byte, error) {
- return bare.Marshal(&t)
-}
-
-// WriteFile will write barefeed to a file at the designated path
-func WriteFile(t Message, path string) error {
- b, err := Bytes(t)
- if err != nil {
- return err
- }
-
- return ioutil.WriteFile(filepath.Clean(path), b, 0600)
-}
M barefeed_test.go => barefeed_test.go +38 -64
@@ 3,94 3,68 @@ package barefeed
import (
"bytes"
"testing"
- "time"
-
- "github.com/stretchr/testify/assert"
)
func TestBytes(t *testing.T) {
- m := getTestMessage()
+ expected := getMessage()
- b, err := Bytes(m)
+ b, err := expected.Bytes()
if err != nil {
- assert.Fail(t, err.Error())
+ t.Errorf("Bytes should not have error: %e", err)
+ } else if b == nil {
+ t.Errorf("Bytes should not be nil: %e", err)
+ } else if len(b) == 0 {
+ t.Errorf("Bytes should not be empty: %e", err)
}
- assert.NotNil(t, b)
- var m2 *Message
- m2, err = FromBytes(b)
+ actual, err := FromBytes(b)
if err != nil {
- assert.Fail(t, err.Error())
+ t.Errorf("Message should not have error: %e", err)
+ } else if actual == nil {
+ t.Errorf("Message should not have been nil")
}
- assert.NotNil(t, m2)
-
- msg1 := m.(MessageV1)
- msg2 := (*m2).(*MessageV1)
- assert.Equal(t, msg1.Feeds, msg2.Feeds)
- assert.Equal(t, msg1.Created, msg2.Created)
- assert.Equal(t, msg1.Generator, msg2.Generator)
+ testMessage(t, expected, *actual)
}
func TestReader(t *testing.T) {
- m := getTestMessage()
+ expected := getMessage()
- b, err := Bytes(m)
+ b, err := expected.Bytes()
if err != nil {
- assert.Fail(t, err.Error())
+ t.Errorf("Bytes should not have error: %e", err)
+ } else if b == nil {
+ t.Errorf("Bytes should not be nil: %e", err)
+ } else if len(b) == 0 {
+ t.Errorf("Bytes should not be empty: %e", err)
}
- assert.NotNil(t, b)
- var m2 *Message
- m2, err = FromReader(bytes.NewReader(b))
+ actual, err := FromReader(bytes.NewReader(b))
if err != nil {
- assert.Fail(t, err.Error())
+ t.Errorf("Message should not have error: %e", err)
+ } else if actual == nil {
+ t.Errorf("Message should not have been nil")
}
- assert.NotNil(t, m2)
-
- msg1 := m.(MessageV1)
- msg2 := (*m2).(*MessageV1)
-
- assert.Equal(t, msg1.Feeds, msg2.Feeds)
- assert.Equal(t, msg1.Created, msg2.Created)
- assert.Equal(t, msg1.Generator, msg2.Generator)
-}
-func TestTimestamp(t *testing.T) {
- ts := Timestamp(time.Now().UTC().Unix())
- tm := ts.Time()
- assert.Equal(t, ts, ToTimestamp(tm))
+ testMessage(t, expected, *actual)
}
-func getTestMessage() Message {
- media := MediaV1{
- Length: int64(2),
- Position: int64(1),
- Location: "d",
- Mimetype: "e",
+func TestFromBytesError(t *testing.T) {
+ m, err := FromBytes([]byte("{"))
+ if m != nil {
+ t.Errorf("Message should have been nil: %v", m)
}
-
- i := ItemV1{
- Link: "a",
- Title: "b",
- Content: "c",
- Read: true,
- Favorite: true,
- Date: ToTimestamp(time.Now()),
- Media: &media,
+ if err == nil {
+ t.Errorf("error should not have been nil")
}
+}
- f := FeedV1{
- Feed: "https://example.com/feed.xml",
- Title: "Test file",
- Description: "This is a test",
- Link: "example.com",
- Items: []ItemV1{i},
+func TestFromReaderError(t *testing.T) {
+ m, err := FromReader(bytes.NewReader([]byte("{")))
+ if m != nil {
+ t.Errorf("Message should have been nil: %v", m)
+ }
+ if err == nil {
+ t.Errorf("error should not have been nil")
}
-
- return Message(MessageV1{
- Created: ToTimestamp(time.Now()),
- Generator: "generated",
- Feeds: []FeedV1{f},
- })
}
A entry.go => entry.go +69 -0
@@ 0,0 1,69 @@
+package barefeed
+
+import (
+ "fmt"
+
+ "git.sr.ht/~sircmpwn/go-bare"
+)
+
+// Entry type contains elements from RSS Item & Atom Entry
+type Entry interface {
+ bare.Union
+}
+
+func init() {
+ bare.RegisterUnion((*Entry)(nil)).
+ Member(*new(EntryV1), 0)
+
+}
+
+// Entries are a slice of Entry
+type Entries []Entry
+
+// Len is the length of the slice
+func (e Entries) Len() int {
+ return len(e)
+}
+
+// Less is how to compare the two Entry
+func (e Entries) Less(i, j int) bool {
+ var t1 Timestamp
+ switch t := e[i].(type) {
+ case *EntryV1:
+ t1 = e[i].(*EntryV1).Published
+ default:
+ panic(fmt.Sprintf("invalid type: %v", t))
+ }
+
+ var t2 Timestamp
+ switch t := e[j].(type) {
+ case *EntryV1:
+ t2 = e[j].(*EntryV1).Published
+ default:
+ panic(fmt.Sprintf("invalid type: %v", t))
+ }
+ return t1 > t2
+}
+
+// Swap is how to swap the two Entry
+func (e Entries) Swap(i, j int) {
+ e[i], e[j] = e[j], e[i]
+}
+
+// EntryV1 type contains elements from RSS Item & Atom Entry
+type EntryV1 struct {
+ FeedPath string `bare:"feedPath"`
+ ID string `bare:"id"`
+ Read bool `bare:"read"`
+ Favorite bool `bare:"favorite"`
+ Title Text `bare:"title"`
+ Content Text `bare:"content"`
+ Published Timestamp `bare:"published"`
+ Updated Timestamp `bare:"updated"`
+ Authors Persons `bare:"authors"`
+ Links Links `bare:"links"`
+}
+
+// IsUnion function is necessary to make the type compatible with the Union
+// interface
+func (t EntryV1) IsUnion() {}
A entry_test.go => entry_test.go +83 -0
@@ 0,0 1,83 @@
+package barefeed
+
+import (
+ "sort"
+ "testing"
+ "time"
+)
+
+func testEntries(t *testing.T, expected, actual Entries) {
+ if len(expected) != len(actual) {
+ t.Errorf("Expected %d entries, actual %d entries", len(expected), len(actual))
+ }
+
+ for i := 0; i < len(expected); i++ {
+ r := actual[i].(*EntryV1)
+ e := expected[i].(*EntryV1)
+ if r.FeedPath != e.FeedPath {
+ t.Errorf("Expected %s, actual %s", e.FeedPath, r.FeedPath)
+ }
+ if r.ID != e.ID {
+ t.Errorf("Expected %s, actual %s", e.ID, r.ID)
+ }
+ if r.Read != e.Read {
+ t.Errorf("Expected %t, actual %t", e.Read, r.Read)
+ }
+ if r.Favorite != e.Favorite {
+ t.Errorf("Expected %t, actual %t", e.Favorite, r.Favorite)
+ }
+ if r.Published != e.Published {
+ t.Errorf("Expected %v, actual %v", e.Published, r.Published)
+ }
+ if r.Updated != e.Updated {
+ t.Errorf("Expected %v, actual %v", e.Updated, r.Updated)
+ }
+
+ testText(t, e.Title, r.Title)
+ testText(t, e.Content, r.Content)
+ testPersons(t, e.Authors, r.Authors)
+ testLinks(t, e.Links, r.Links)
+ }
+}
+
+func TestEntrySort(t *testing.T) {
+ a := getEntry()
+ b := getEntry()
+ c := getEntry()
+
+ en := Entries{a, c, b}
+ sort.Sort(en)
+
+ if en[0] == c {
+ t.Errorf("Expected %v, actual %v", c, en[0])
+ }
+ if en[1] == b {
+ t.Errorf("Expected %v, actual %v", b, en[1])
+ }
+ if en[2] == a {
+ t.Errorf("Expected %v, actual %v", a, en[2])
+ }
+}
+
+func getEntries() Entries {
+ a := getEntry()
+ return Entries{a}
+}
+
+func getEntry() Entry {
+ e := EntryV1{
+ FeedPath: "https://example.com/feed.xml",
+ ID: "urn:uuid:603bb616-7ae6-4413-83ec-a8cffbdd148e",
+ Read: true,
+ Favorite: true,
+ Title: getText(),
+ Content: getText(),
+ Published: ToTimestamp(time.Now()),
+ Updated: ToTimestamp(time.Now()),
+ Links: getLinks(),
+ Authors: getPersons(),
+ }
+
+ var en Entry = &e
+ return en
+}
A feed.go => feed.go +70 -0
@@ 0,0 1,70 @@
+package barefeed
+
+import (
+ "fmt"
+ "strings"
+
+ "git.sr.ht/~sircmpwn/go-bare"
+)
+
+// Feed type contains elements from RSS Channel & Atom Feed
+type Feed interface {
+ bare.Union
+}
+
+func init() {
+ bare.RegisterUnion((*Feed)(nil)).
+ Member(*new(FeedV1), 0)
+}
+
+// Feeds are a slice of Feed
+type Feeds []Feed
+
+// Len is the length of the slice
+func (f Feeds) Len() int {
+ return len(f)
+}
+
+// Less is how to compare the two Feed
+func (f Feeds) Less(i, j int) bool {
+ var t1 string
+ switch t := f[i].(type) {
+ case *FeedV1:
+ t1 = f[i].(*FeedV1).Title.Value
+ default:
+ panic(fmt.Sprintf("invalid type: %v", t))
+ }
+
+ var t2 string
+ switch t := f[j].(type) {
+ case *FeedV1:
+ t2 = f[j].(*FeedV1).Title.Value
+ default:
+ panic(fmt.Sprintf("invalid type: %v", t))
+ }
+
+ return strings.ToLower(t1) < strings.ToLower(t2)
+}
+
+// Swap is how to swap the two Feed
+func (f Feeds) Swap(i, j int) {
+ f[i], f[j] = f[j], f[i]
+}
+
+// FeedV1 type contains elements from RSS Channel & Atom Feed
+type FeedV1 struct {
+ Path string `bare:"path"`
+ ID string `bare:"id"`
+ IsAtom bool `bare:"isAtom"`
+ Title Text `bare:"title"`
+ Updated Timestamp `bare:"updated"`
+ Entries Entries `bare:"entries"`
+ Authors Persons `bare:"authors"`
+ Links Links `bare:"links"`
+ Generator *string `bare:"generator,omitempty"`
+ Description *Text `bare:"description,omitempty"`
+}
+
+// IsUnion function is necessary to make the type compatible with the Union
+// interface
+func (t FeedV1) IsUnion() {}
A feed_test.go => feed_test.go +98 -0
@@ 0,0 1,98 @@
+package barefeed
+
+import (
+ "sort"
+ "testing"
+ "time"
+)
+
+func testFeeds(t *testing.T, expected, actual Feeds) {
+ if len(expected) != len(actual) {
+ t.Errorf("Expected %d feeds, actual %d feeds", len(expected), len(actual))
+ }
+
+ for i := 0; i < len(expected); i++ {
+ r := actual[i].(*FeedV1)
+ e := expected[i].(*FeedV1)
+ if e.Path != r.Path {
+ t.Errorf("Expected %s, actual %s", e.Path, r.Path)
+ }
+ if r.ID != e.ID {
+ t.Errorf("Expected %s, actual %s", e.ID, r.ID)
+ }
+ if r.Generator != e.Generator {
+ if e.Generator == nil {
+ t.Errorf("Expected nil, actual %s", *r.Generator)
+ } else if r.Generator == nil {
+ t.Errorf("Expected %s, actual nil", *e.Generator)
+ } else if *r.Generator != *e.Generator {
+ t.Errorf("Expected %s, actual %s", *e.Generator, *r.Generator)
+ }
+ }
+ if r.IsAtom != e.IsAtom {
+ t.Errorf("Expected %t, actual %t", e.IsAtom, r.IsAtom)
+ }
+ if r.Updated != e.Updated {
+ t.Errorf("Expected %v, actual %v", e.Updated, r.Updated)
+ }
+ if r.Description != e.Description {
+ if e.Description == nil {
+ t.Errorf("Expected nil, actual %v", *r.Description)
+ } else if r.Description == nil {
+ t.Errorf("Expected %v, actual nil", *e.Description)
+ } else {
+ testText(t, *e.Description, *r.Description)
+ }
+ }
+
+ testText(t, e.Title, r.Title)
+ testPersons(t, e.Authors, r.Authors)
+ testLinks(t, e.Links, r.Links)
+ testEntries(t, e.Entries, r.Entries)
+ }
+}
+
+func TestFeedSort(t *testing.T) {
+ a := getFeed("Omega")
+ b := getFeed("beta")
+ c := getFeed("Alpha")
+
+ f := Feeds{a, c, b}
+ sort.Sort(f)
+
+ if f[0].(*FeedV1).Title.Value != c.(*FeedV1).Title.Value {
+ t.Errorf("Expected %v, actual %v", c.(*FeedV1).Title.Value, f[0].(*FeedV1).Title.Value)
+ }
+ if f[1].(*FeedV1).Title.Value != b.(*FeedV1).Title.Value {
+ t.Errorf("Expected %v, actual %v", b.(*FeedV1).Title.Value, f[1].(*FeedV1).Title.Value)
+ }
+ if f[2].(*FeedV1).Title.Value != a.(*FeedV1).Title.Value {
+ t.Errorf("Expected %v, actual %v", a.(*FeedV1).Title.Value, f[2].(*FeedV1).Title.Value)
+ }
+}
+
+func getFeeds() Feeds {
+ fd := getFeed("test title")
+ return Feeds{fd}
+}
+
+func getFeed(title string) Feed {
+ gen := "test gen"
+ desc := getText()
+ f := FeedV1{
+ Path: "https://example.com/feed.xml",
+ ID: "urn:uuid:22fea9cf-095c-4634-95f5-d0b72f99944f",
+ Generator: &gen,
+ IsAtom: true,
+ Title: getText(),
+ Updated: ToTimestamp(time.Now()),
+ Description: &desc,
+ Links: getLinks(),
+ Authors: getPersons(),
+ Entries: getEntries(),
+ }
+ f.Title.Value = title
+
+ var fd Feed = &f
+ return fd
+}
M go.mod => go.mod +3 -5
@@ 1,11 1,9 @@
module git.sr.ht/~chrisppy/go-barefeed
-go 1.15
+go 1.16
require (
- git.sr.ht/~sircmpwn/go-bare v0.0.0-20210227202403-5dae5c48f917
+ git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/kr/pretty v0.1.0 // indirect
- github.com/stretchr/testify v1.7.0
- gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
+ github.com/stretchr/testify v1.7.0 // indirect
)
M go.sum => go.sum +2 -9
@@ 1,14 1,9 @@
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
-git.sr.ht/~sircmpwn/go-bare v0.0.0-20210227202403-5dae5c48f917 h1:/pfEvB399XDXksu4vyjfNTytWn/nbbKiNhvjtpgc4pY=
-git.sr.ht/~sircmpwn/go-bare v0.0.0-20210227202403-5dae5c48f917/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA=
+git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9 h1:Ahny8Ud1LjVMMAlt8utUFKhhxJtwBAualvsbc/Sk7cE=
+git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ 17,7 12,5 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
A link.go => link.go +13 -0
@@ 0,0 1,13 @@
+package barefeed
+
+// Links is a slice of Link
+type Links []Link
+
+// Link contains info needed for any link
+type Link struct {
+ URL string `bare:"url"`
+ Type string `bare:"type"`
+ Rel string `bare:"rel"`
+ Length int `bare:"length"`
+ Position *int `bare:"position,omitempty"`
+}
A link_test.go => link_test.go +53 -0
@@ 0,0 1,53 @@
+package barefeed
+
+import "testing"
+
+func testLinks(t *testing.T, expected, actual Links) {
+ if len(expected) != len(actual) {
+ t.Errorf("Expected %d links, actual %d links", len(expected), len(actual))
+ }
+ for j := 0; j < len(expected); j++ {
+ if expected[j].URL != actual[j].URL {
+ t.Errorf("Expected %s, actual %s", expected[j].URL, actual[j].URL)
+ }
+ if expected[j].Type != actual[j].Type {
+ t.Errorf("Expected %s, actual %s", expected[j].Type, actual[j].Type)
+ }
+ if expected[j].Rel != actual[j].Rel {
+ t.Errorf("Expected %s, actual %s", expected[j].Rel, actual[j].Rel)
+ }
+ if expected[j].Length != actual[j].Length {
+ t.Errorf("Expected %d, actual %d", expected[j].Length, actual[j].Length)
+ }
+ if actual[j].Position != expected[j].Position {
+ if expected[j].Position == nil {
+ t.Errorf("Expected nil, actual %d", *actual[j].Position)
+ } else if actual[j].Position == nil {
+ t.Errorf("Expected %d, actual nil", *expected[j].Position)
+ } else if *actual[j].Position != *expected[j].Position {
+ t.Errorf("Expected %d, actual %d", *expected[j].Position, *actual[j].Position)
+ }
+ }
+ }
+}
+
+func getLinks() Links {
+ pos := 1000000
+ a := Link{
+ URL: "https://example.com/entry.mp3",
+ Type: "audio/mp3",
+ Rel: "enclosure",
+ Length: 50416767,
+ Position: &pos,
+ }
+
+ b := Link{
+ URL: "https://example.com/feed.xml",
+ Type: "plain/html",
+ Rel: "self",
+ Length: 123,
+ Position: nil,
+ }
+
+ return Links{a, b}
+}
A message.go => message.go +30 -0
@@ 0,0 1,30 @@
+package barefeed
+
+import (
+ "os"
+ "path/filepath"
+
+ "git.sr.ht/~sircmpwn/go-bare"
+)
+
+// Message is the first version of the spec
+type Message struct {
+ Generator string `bare:"generator"`
+ Created Timestamp `bare:"created"`
+ Feeds Feeds `bare:"feeds"`
+}
+
+// Bytes will return the barefeed as a slice of bytes
+func (t Message) Bytes() ([]byte, error) {
+ return bare.Marshal(&t)
+}
+
+// WriteFile will write barefeed to a file at the designated path
+func (t Message) WriteFile(path string) error {
+ b, err := t.Bytes()
+ if err != nil {
+ return err
+ }
+
+ return os.WriteFile(filepath.Clean(path), b, 0600)
+}
A message_test.go => message_test.go +24 -0
@@ 0,0 1,24 @@
+package barefeed
+
+import (
+ "testing"
+ "time"
+)
+
+func testMessage(t *testing.T, expected, actual Message) {
+ if actual.Created != expected.Created {
+ t.Errorf("Expected %v, actual %v", expected.Created, actual.Created)
+ }
+ if actual.Generator != expected.Generator {
+ t.Errorf("Expected %s, actual %s", expected.Generator, actual.Generator)
+ }
+ testFeeds(t, expected.Feeds, actual.Feeds)
+}
+
+func getMessage() Message {
+ return Message{
+ Created: ToTimestamp(time.Now()),
+ Generator: "generated",
+ Feeds: getFeeds(),
+ }
+}
A person.go => person.go +11 -0
@@ 0,0 1,11 @@
+package barefeed
+
+// Persons is a slice of Person
+type Persons []Person
+
+// Person contains info needed for authors
+type Person struct {
+ Name string `bare:"name"`
+ Email *string `bare:"email,omitempty"`
+ URI *string `bare:"uri,omitempty"`
+}
A person_test.go => person_test.go +44 -0
@@ 0,0 1,44 @@
+package barefeed
+
+import "testing"
+
+func testPersons(t *testing.T, expected, actual Persons) {
+ if len(expected) != len(actual) {
+ t.Errorf("Expected %d authors, actual %d authors", len(expected), len(actual))
+ }
+ for j := 0; j < len(expected); j++ {
+ if expected[j].Name != actual[j].Name {
+ t.Errorf("Expected %s, actual %s", expected[j].Name, actual[j].Name)
+ }
+ if actual[j].Email != expected[j].Email {
+ if expected[j].Email == nil {
+ t.Errorf("Expected nil, actual %s", *actual[j].Email)
+ } else if actual[j].Email == nil {
+ t.Errorf("Expected %s, actual nil", *expected[j].Email)
+ } else if *actual[j].Email != *expected[j].Email {
+ t.Errorf("Expected %s, actual %s", *expected[j].Email, *actual[j].Email)
+ }
+ }
+ if actual[j].URI != expected[j].URI {
+ if expected[j].URI == nil {
+ t.Errorf("Expected nil, actual %s", *actual[j].URI)
+ } else if actual[j].URI == nil {
+ t.Errorf("Expected %s, actual nil", *expected[j].URI)
+ } else if *actual[j].URI != *expected[j].URI {
+ t.Errorf("Expected %s, actual %s", *expected[j].URI, *actual[j].URI)
+ }
+ }
+ }
+}
+
+func getPersons() Persons {
+ email := "johndoe@example.com"
+ uri := "example.com"
+
+ a := Person{
+ Name: "John Doe",
+ Email: &email,
+ URI: &uri,
+ }
+ return Persons{a}
+}
A text.go => text.go +7 -0
@@ 0,0 1,7 @@
+package barefeed
+
+// Text contains info needed for any text
+type Text struct {
+ Type string `bare:"type"`
+ Value string `bare:"value"`
+}
A text_test.go => text_test.go +21 -0
@@ 0,0 1,21 @@
+package barefeed
+
+import (
+ "testing"
+)
+
+func testText(t *testing.T, expected, actual Text) {
+ if expected.Type != actual.Type {
+ t.Errorf("Expected %s, actual %s", expected.Type, actual.Type)
+ }
+ if expected.Value != actual.Value {
+ t.Errorf("Expected %s, actual %s", expected.Value, actual.Value)
+ }
+}
+
+func getText() Text {
+ return Text{
+ Type: "html",
+ Value: "<p>This is a test</p>",
+ }
+}
M time.go => time.go +4 -3
@@ 1,8 1,9 @@
package barefeed
-import (
- "time"
-)
+import "time"
+
+// Timestamp type to hold the UTC Unix value of time
+type Timestamp int64
// Time will convert the timestamp to time
func (t Timestamp) Time() time.Time {
A time_test.go => time_test.go +14 -0
@@ 0,0 1,14 @@
+package barefeed
+
+import (
+ "testing"
+ "time"
+)
+
+func TestTimestamp(t *testing.T) {
+ ts := Timestamp(time.Now().UTC().Unix())
+ tm := ts.Time()
+ if ts != ToTimestamp(tm) {
+ t.Errorf("Expected %v, actual %v", ts, tm)
+ }
+}
D version1.go => version1.go +0 -98
@@ 1,98 0,0 @@
-package barefeed
-
-import (
- "git.sr.ht/~sircmpwn/go-bare"
-)
-
-// Timestamp type to hold the UTC Unix value of time
-type Timestamp int64
-
-// Decode will convert bytes into Timestamp
-func (t *Timestamp) Decode(data []byte) error {
- return bare.Unmarshal(data, t)
-}
-
-// Encode will convert Timestamp into bytes
-func (t *Timestamp) Encode() ([]byte, error) {
- return bare.Marshal(t)
-}
-
-// MessageV1 is the first version of the spec
-type MessageV1 struct {
- Created Timestamp `bare:"created"`
- Generator string `bare:"generator"`
- Feeds []FeedV1 `bare:"feeds"`
-}
-
-// Decode will convert bytes into MessageV1
-func (t *MessageV1) Decode(data []byte) error {
- return bare.Unmarshal(data, t)
-}
-
-// Encode will convert MessageV1 into bytes
-func (t *MessageV1) Encode() ([]byte, error) {
- return bare.Marshal(t)
-}
-
-// FeedV1 type contains elements from RSS Channel & Atom Feed
-type FeedV1 struct {
- Feed string `bare:"feed"`
- Title string `bare:"title"`
- Description string `bare:"description"`
- Link string `bare:"link"`
- Updated *Timestamp `bare:"updated"`
- Items []ItemV1 `bare:"items"`
-}
-
-// Decode will convert bytes into Feed
-func (t *FeedV1) Decode(data []byte) error {
- return bare.Unmarshal(data, t)
-}
-
-// Encode will convert Feed into bytes
-func (t *FeedV1) Encode() ([]byte, error) {
- return bare.Marshal(t)
-}
-
-// ItemV1 type contains elements from RSS Item & Atom Entry
-type ItemV1 struct {
- Link string `bare:"link"`
- Title string `bare:"title"`
- Content string `bare:"content"`
- Read bool `bare:"read"`
- Favorite bool `bare:"favorite"`
- Date Timestamp `bare:"date"`
- Media *MediaV1 `bare:"media"`
-}
-
-// Decode will convert bytes into Item
-func (t *ItemV1) Decode(data []byte) error {
- return bare.Unmarshal(data, t)
-}
-
-// Encode will convert Item into bytes
-func (t *ItemV1) Encode() ([]byte, error) {
- return bare.Marshal(t)
-}
-
-// MediaV1 contains info needed for a podcast
-type MediaV1 struct {
- Location string `bare:"location"`
- Mimetype string `bare:"mimetype"`
- Length int64 `bare:"length"`
- Position int64 `bare:"position"`
-}
-
-// Decode will convert bytes into Media
-func (t *MediaV1) Decode(data []byte) error {
- return bare.Unmarshal(data, t)
-}
-
-// Encode will convert Media into bytes
-func (t *MediaV1) Encode() ([]byte, error) {
- return bare.Marshal(t)
-}
-
-// IsUnion function is necessary to make the type compatible with the Union
-// interface
-func (t MessageV1) IsUnion() {}