Skip to content

Commit

Permalink
Package restructure
Browse files Browse the repository at this point in the history
The package has been moved around to give better structure. Individual
components have been put into separate files.
  • Loading branch information
SlyMarbo committed Mar 15, 2013
1 parent f95022c commit 8d658ef
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 249 deletions.
91 changes: 91 additions & 0 deletions ISO-8859-1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package rss

import (
"bytes"
"errors"
"io"
"strings"
"unicode/utf8"
)

// ISO-8859-1 support

type charsetISO88591er struct {
r io.ByteReader
buf *bytes.Buffer
}

func newCharsetISO88591(r io.Reader) *charsetISO88591er {
buf := bytes.NewBuffer(make([]byte, 0, utf8.UTFMax))
return &charsetISO88591er{r.(io.ByteReader), buf}
}

func (cs *charsetISO88591er) ReadByte() (b byte, err error) {
// http://unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
// Date: 1999 July 27; Last modified: 27-Feb-2001 05:08
if cs.buf.Len() <= 0 {
r, err := cs.r.ReadByte()
if err != nil {
return 0, err
}
if r < utf8.RuneSelf {
return r, nil
}
cs.buf.WriteRune(rune(r))
}
return cs.buf.ReadByte()
}

func (cs *charsetISO88591er) Read(p []byte) (int, error) {
// Use ReadByte method.
return 0, errors.New("Use ReadByte()")
}

func isCharset(charset string, names []string) bool {
charset = strings.ToLower(charset)
for _, n := range names {
if charset == strings.ToLower(n) {
return true
}
}
return false
}

func isCharsetISO88591(charset string) bool {
// http://www.iana.org/assignments/character-sets
// (last updated 2010-11-04)
names := []string{
// Name
"ISO_8859-1:1987",
// Alias (preferred MIME name)
"ISO-8859-1",
// Aliases
"iso-ir-100",
"ISO_8859-1",
"latin1",
"l1",
"IBM819",
"CP819",
"csISOLatin1",
}
return isCharset(charset, names)
}

func isCharsetUTF8(charset string) bool {
names := []string{
"UTF-8",
// Default
"",
}
return isCharset(charset, names)
}

func charsetReader(charset string, input io.Reader) (io.Reader, error) {
switch {
case isCharsetUTF8(charset):
return input, nil
case isCharsetISO88591(charset):
return newCharsetISO88591(input), nil
}
return nil, errors.New("CharsetReader: unexpected charset: " + charset)
}
36 changes: 36 additions & 0 deletions database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package rss

var database *db

func init() {
database = NewDB()
go database.Run()
}

type db struct {
req chan string
res chan bool
known map[string]struct{}
}

func (d *db) Run() {
d.known = make(map[string]struct{})
var s string

for {
s = <- d.req
if _, ok := d.known[s]; ok {
d.res <- true
} else {
d.res <- false
d.known[s] = struct{}{}
}
}
}

func NewDB() *db {
out := new(db)
out.req = make(chan string)
out.res = make(chan bool)
return out
}
133 changes: 133 additions & 0 deletions rss 2.0.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package rss

import (
"bytes"
"encoding/xml"
"fmt"
"sort"
"strings"
"time"
)

func parseRSS2(data []byte, read *db) (*Feed, error) {
feed := rss2_0Feed{}
p := xml.NewDecoder(bytes.NewReader(data))
p.CharsetReader = charsetReader
err := p.Decode(&feed)
if err != nil {
return nil, err
}
if feed.Channel == nil {
return nil, fmt.Errorf("Error: no channel found in %q.", string(data))
}

channel := feed.Channel

out := new(Feed)
out.Title = channel.Title
out.Description = channel.Description
out.Link = channel.Link
out.Image = channel.Image.Image()
if channel.MinsToLive != 0 {
sort.Ints(channel.SkipHours)
next := time.Now().Add(time.Duration(channel.MinsToLive) * time.Minute)
for _, hour := range channel.SkipHours {
if hour == next.Hour() {
next.Add(time.Duration(60 - next.Minute()) * time.Minute)
}
}
trying := true
for trying {
trying = false
for _, day := range channel.SkipDays {
if strings.Title(day) == next.Weekday().String() {
next.Add(time.Duration(24 - next.Hour()) * time.Hour)
trying = true
break
}
}
}

out.Refresh = next
}

if out.Refresh.IsZero() {
out.Refresh = time.Now().Add(10 * time.Minute)
}

if channel.Items == nil {
return nil, fmt.Errorf("Error: no feeds found in %q.", string(data))
}

out.Items = make([]*Item, 0, len(channel.Items))

// Process items.
for _, item := range channel.Items {

// Skip items already known.
if read.req <- item.ID; <- read.res {
continue
}

next := new(Item)
next.Title = item.Title
next.Content = item.Content
next.Link = item.Link
if item.Date != "" {
next.Date, err = parseTime(item.Date)
if err != nil {
return nil, err
}
}
next.ID = item.ID
next.Read = false

out.Items = append(out.Items, next)
out.Unread++
}

return out, nil
}

type rss2_0Feed struct {
XMLName xml.Name `xml:"rss"`
Channel *rss2_0Channel `xml:"channel"`
}

type rss2_0Channel struct {
XMLName xml.Name `xml:"channel"`
Title string `xml:"title"`
Description string `xml:"description"`
Link string `xml:"link"`
Image rss2_0Image `xml:"image"`
Items []rss2_0Item `xml:"item"`
MinsToLive int `xml:"ttl"`
SkipHours []int `xml:"skipHours>hour"`
SkipDays []string `xml:"skipDays>day"`
}

type rss2_0Item struct {
XMLName xml.Name `xml:"item"`
Title string `xml:"title"`
Content string `xml:"description"`
Link string `xml:"link"`
Date string `xml:"pubDate"`
ID string `xml:"guid"`
}

type rss2_0Image struct {
XMLName xml.Name `xml:"image"`
Title string `xml:"title"`
Url string `xml:"url"`
Height int `xml:"height"`
Width int `xml:"width"`
}

func (i *rss2_0Image) Image() *Image {
out := new(Image)
out.Title = i.Title
out.Url = i.Url
out.Height = uint32(i.Height)
out.Width = uint32(i.Width)
return out
}
Loading

0 comments on commit 8d658ef

Please sign in to comment.