~chrisppy/go-barefeed

63fc114fd6c8a8779e44c7042a5c1a6f543a76fb — Chris Palmer 2 months ago 022d6ea v0.3.0
updates from schema
11 files changed, 177 insertions(+), 57 deletions(-)

M entry.go
M entry_test.go
M feed.go
M feed_test.go
M link.go
M link_test.go
M message.go
M message_test.go
M person.go
M text.go
M text_test.go
M entry.go => entry.go +0 -2
@@ 54,8 54,6 @@ func (e Entries) Swap(i, j int) {
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"`

M entry_test.go => entry_test.go +33 -11
@@ 6,26 6,39 @@ import (
	"time"
)

func testEntries(t *testing.T, expected, actual Entries) {
func testEntryList(t *testing.T, expected, actual []string) {
	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)
		r := actual[i]
		e := expected[i]
		if r != e {
			t.Errorf("Expected %s, actual %s", e, r)
		}
	}
}

func testEntries(t *testing.T, expected, actual map[string]Entry) {
	if len(expected) != len(actual) {
		t.Errorf("Expected %d entries, actual %d entries", len(expected), len(actual))
	}

	for k, ev := range expected {
		rv, ok := actual[k]
		if !ok {
			t.Errorf("expected key %s not found", k)
		}

		r := rv.(*EntryV1)
		e := ev.(*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)
		}


@@ 64,12 77,21 @@ func getEntries() Entries {
	return Entries{a}
}

func getEntryList() []string {
	e := getEntries()

	r := make([]string, len(e))
	for i, a := range e {
		r[i] = a.(*EntryV1).ID
	}

	return r
}

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()),

M feed.go => feed.go +34 -4
@@ 7,6 7,16 @@ import (
	"git.sr.ht/~sircmpwn/go-bare"
)

// FeedType is an enumerated type for Feed
type FeedType uint

const (
	// RSS denotes the feed as RSS
	RSS FeedType = iota
	// ATOM denotes the feed as ATOM
	ATOM
)

// Feed type contains elements from RSS Channel & Atom Feed
type Feed interface {
	bare.Union


@@ 55,16 65,36 @@ func (f Feeds) Swap(i, j int) {
type FeedV1 struct {
	Path        string    `bare:"path"`
	ID          string    `bare:"id"`
	IsAtom      bool      `bare:"isAtom"`
	FeedType    FeedType  `bare:"feedType"`
	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"`
	Entries     []string  `bare:"entries"`
	Generator   *string   `bare:"generator"`
	Description *Text     `bare:"description"`
}

// IsUnion function is necessary to make the type compatible with the Union
// interface
func (t FeedV1) IsUnion() {}

// String converts the enumerated value to a string.  "RSS" is default.
func (t FeedType) String() string {
	switch t {
	case ATOM:
		return "ATOM"
	default:
		return "RSS"
	}
}

// ToFeedType converts a string to the enumerated value.  RSS is default.
func ToFeedType(t string) FeedType {
	switch t {
	case "ATOM":
		return ATOM
	default:
		return RSS
	}
}

M feed_test.go => feed_test.go +34 -11
@@ 6,14 6,19 @@ import (
	"time"
)

func testFeeds(t *testing.T, expected, actual Feeds) {
func testFeeds(t *testing.T, expected, actual map[string]Feed) {
	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)
	for k, ev := range expected {
		rv, ok := actual[k]
		if !ok {
			t.Errorf("expected key %s not found", k)
		}

		r := rv.(*FeedV1)
		e := ev.(*FeedV1)
		if e.Path != r.Path {
			t.Errorf("Expected %s, actual %s", e.Path, r.Path)
		}


@@ 29,8 34,8 @@ func testFeeds(t *testing.T, expected, actual Feeds) {
				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.FeedType != e.FeedType {
			t.Errorf("Expected %d, actual %d", e.FeedType, r.FeedType)
		}
		if r.Updated != e.Updated {
			t.Errorf("Expected %v, actual %v", e.Updated, r.Updated)


@@ 48,7 53,7 @@ func testFeeds(t *testing.T, expected, actual Feeds) {
		testText(t, e.Title, r.Title)
		testPersons(t, e.Authors, r.Authors)
		testLinks(t, e.Links, r.Links)
		testEntries(t, e.Entries, r.Entries)
		testEntryList(t, e.Entries, r.Entries)
	}
}



@@ 71,9 76,13 @@ func TestFeedSort(t *testing.T) {
	}
}

func getFeeds() Feeds {
func getFeeds() map[string]Feed {
	m := make(map[string]Feed)

	fd := getFeed("test title")
	return Feeds{fd}
	m[fd.(*FeedV1).Path] = fd

	return m
}

func getFeed(title string) Feed {


@@ 83,16 92,30 @@ func getFeed(title string) Feed {
		Path:        "https://example.com/feed.xml",
		ID:          "urn:uuid:22fea9cf-095c-4634-95f5-d0b72f99944f",
		Generator:   &gen,
		IsAtom:      true,
		FeedType:    ATOM,
		Title:       getText(),
		Updated:     ToTimestamp(time.Now()),
		Description: &desc,
		Links:       getLinks(),
		Authors:     getPersons(),
		Entries:     getEntries(),
		Entries:     getEntryList(),
	}
	f.Title.Value = title

	var fd Feed = &f
	return fd
}

func TestFeedType(t *testing.T) {
	e := RSS
	a := ToFeedType(e.String())
	if e != a {
		t.Errorf("Expected %d, actual %d", e, a)
	}

	e = ATOM
	a = ToFeedType(e.String())
	if e != a {
		t.Errorf("Expected %d, actual %d", e, a)
	}
}

M link.go => link.go +1 -2
@@ 6,8 6,7 @@ type Links []Link
// Link contains info needed for any link
type Link struct {
	URL      string `bare:"url"`
	Type     string `bare:"type"`
	LinkType string `bare:"linkType"`
	Rel      string `bare:"rel"`
	Length   int    `bare:"length"`
	Position *int   `bare:"position,omitempty"`
}

M link_test.go => link_test.go +4 -16
@@ 10,8 10,8 @@ func testLinks(t *testing.T, expected, actual Links) {
		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].LinkType != actual[j].LinkType {
			t.Errorf("Expected %s, actual %s", expected[j].LinkType, actual[j].LinkType)
		}
		if expected[j].Rel != actual[j].Rel {
			t.Errorf("Expected %s, actual %s", expected[j].Rel, actual[j].Rel)


@@ 19,34 19,22 @@ func testLinks(t *testing.T, expected, actual Links) {
		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",
		LinkType: "audio/mp3",
		Rel:      "enclosure",
		Length:   50416767,
		Position: &pos,
	}

	b := Link{
		URL:      "https://example.com/feed.xml",
		Type:     "plain/html",
		LinkType: "plain/html",
		Rel:      "self",
		Length:   123,
		Position: nil,
	}

	return Links{a, b}

M message.go => message.go +6 -3
@@ 9,9 9,12 @@ import (

// Message is the first version of the spec
type Message struct {
	Generator string    `bare:"generator"`
	Created   Timestamp `bare:"created"`
	Feeds     Feeds     `bare:"feeds"`
	Generator string               `bare:"generator"`
	Created   Timestamp            `bare:"created"`
	Feeds     map[string]Feed      `bare:"feeds"`
	Entries   map[string]Entry     `bare:"entries"`
	Unread    map[string]int64     `bare:"unread"`
	Favorite  map[string]Timestamp `bare:"favorite"`
}

// Bytes will return the barefeed as a slice of bytes

M message_test.go => message_test.go +1 -0
@@ 13,6 13,7 @@ func testMessage(t *testing.T, expected, actual Message) {
		t.Errorf("Expected %s, actual %s", expected.Generator, actual.Generator)
	}
	testFeeds(t, expected.Feeds, actual.Feeds)
	testEntries(t, expected.Entries, actual.Entries)
}

func getMessage() Message {

M person.go => person.go +2 -2
@@ 6,6 6,6 @@ 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"`
	Email *string `bare:"email"`
	URI   *string `bare:"uri"`
}

M text.go => text.go +38 -2
@@ 1,7 1,43 @@
package barefeed

// TextType is an enumerated type for Text
type TextType uint

const (
	// TEXT denotes the text as plain text
	TEXT TextType = iota
	// HTML denotes the text as entity escaped html
	HTML
	// XHTML denotes the text as inline xhtml, wrapped in a div element
	XHTML
)

// Text contains info needed for any text
type Text struct {
	Type  string `bare:"type"`
	Value string `bare:"value"`
	Value    string   `bare:"value"`
	TextType TextType `bare:"textType"`
}

// String converts the enumerated value to a string.  "TEXT" is default.
func (t TextType) String() string {
	switch t {
	case HTML:
		return "HTML"
	case XHTML:
		return "XHTML"
	default:
		return "TEXT"
	}
}

// ToTextType converts a string to the enumerated value.  TEXT is default.
func ToTextType(t string) TextType {
	switch t {
	case "HTML":
		return HTML
	case "XHTML":
		return XHTML
	default:
		return TEXT
	}
}

M text_test.go => text_test.go +24 -4
@@ 5,8 5,8 @@ import (
)

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.TextType != actual.TextType {
		t.Errorf("Expected %d, actual %d", expected.TextType, actual.TextType)
	}
	if expected.Value != actual.Value {
		t.Errorf("Expected %s, actual %s", expected.Value, actual.Value)


@@ 15,7 15,27 @@ func testText(t *testing.T, expected, actual Text) {

func getText() Text {
	return Text{
		Type:  "html",
		Value: "<p>This is a test</p>",
		TextType: HTML,
		Value:    "<p>This is a test</p>",
	}
}

func TestTextType(t *testing.T) {
	e := TEXT
	a := ToTextType(e.String())
	if e != a {
		t.Errorf("Expected %d, actual %d", e, a)
	}

	e = HTML
	a = ToTextType(e.String())
	if e != a {
		t.Errorf("Expected %d, actual %d", e, a)
	}

	e = XHTML
	a = ToTextType(e.String())
	if e != a {
		t.Errorf("Expected %d, actual %d", e, a)
	}
}