diff --git a/channel.go b/channel.go new file mode 100644 index 0000000..9838235 --- /dev/null +++ b/channel.go @@ -0,0 +1,104 @@ +package rss + +import ( + "encoding/xml" + "fmt" + "sync" + "time" +) + +type Channel struct { + addCategory chan string + addItem chan *Item + XMLName xml.Name `xml:"channel"` + Title string `xml:"title"` + Link string `xml:"link"` + Description string `xml:"description"` + Language string `xml:"language,omitempty"` + Copyright string `xml:"copyright,omitempty"` + ManagingEditor string `xml:"managingEditor,omitempty"` + WebMaster string `xml:"webMaster,omitempty"` + PubDate string `xml:"pubDate,omitempty"` + LastBuildDate string `xml:"lastBuildDate,omitempty"` + Categories []string `xml:"category,omitempty"` + Generator string `xml:"generator,omitempty"` + Docs string `xml:"docs,omitempty"` + Cloud string `xml:"cloud,omitempty"` + Image *Image + Rating string `xml:"rating,omitempty"` + TextInput *TextInput + SkipHours []int `xml:"skipHours,omitempty"` + SkipDays []int `xml:"skipDays,omitempty"` + Items []*Item + Ttl int `xml:"ttl,omitempty"` + wg sync.WaitGroup +} + +func (c *Channel) start() { + c.wg.Done() + + for { + select { + case category := <-c.addCategory: + c.Categories = append(c.Categories, category) + case item := <-c.addItem: + c.Items = append(c.Items, item) + } + + c.LastBuildDate = time.Now().Format(time.RFC1123Z) + } +} + +func initChannel() *Channel { + now := time.Now().Format(time.RFC1123Z) + + channel := &Channel{ + PubDate: now, + LastBuildDate: now, + } + + channel.wg.Add(1) + go channel.start() + channel.wg.Wait() + + return channel +} + +func (c *Channel) check() error { + if len(c.Title) == 0 { + return fmt.Errorf("error: title not set") + } + if len(c.Link) == 0 { + return fmt.Errorf("error: link not set") + } + if len(c.Description) == 0 { + return fmt.Errorf("error: description not set") + } + + if err := c.Image.check(); err != nil { + return fmt.Errorf("error checking image: %v", err) + } + if err := c.TextInput.check(); err != nil { + return fmt.Errorf("error checking textInput: %v", err) + } + + for _, item := range c.Items { + if err := item.check(); err != nil { + return fmt.Errorf("error checking item: %v", err) + } + } + + return nil +} + +func NewChannel() *Channel { + return initChannel() +} + +func (c *Channel) AddCategory(category string) { + c.addCategory <- category +} + +func (c *Channel) AddItem(i *Item) { + c.addItem <- i +} diff --git a/feed.go b/feed.go new file mode 100644 index 0000000..333f204 --- /dev/null +++ b/feed.go @@ -0,0 +1,60 @@ +package rss + +import ( + "encoding/xml" + "fmt" + "sync" +) + +type Feed struct { + addChannel chan *Channel + XMLName xml.Name `xml:"rss"` + Version string `xml:"version,attr"` + ContentNamespace string `xml:"xmlns:content,attr"` + Channels []*Channel + wg sync.WaitGroup +} + +func (f *Feed) start() { + f.wg.Done() + + for channel := range f.addChannel { + f.Channels = append(f.Channels, channel) + } +} + +func initFeed() *Feed { + feed := &Feed{ + addChannel: make(chan *Channel), + Version: "2.0", + ContentNamespace: "http://purl.org/rss/1.0/modules/content/", + } + + feed.wg.Add(1) + go feed.start() + feed.wg.Wait() + + return feed +} + +func (f *Feed) check() error { + if f.Channels == nil { + return fmt.Errorf("error: feed has no channels") + } + + for _, channel := range f.Channels { + if err := channel.check(); err != nil { + return fmt.Errorf("error checking channel: %v", err) + } + } + + return nil +} + +func NewFeed() *Feed { + return initFeed() +} + +func (f *Feed) AddChannel(c *Channel) { + f.addChannel <- c +} diff --git a/item.go b/item.go new file mode 100644 index 0000000..142e63e --- /dev/null +++ b/item.go @@ -0,0 +1,68 @@ +package rss + +import ( + "encoding/xml" + "fmt" + "sync" + "time" +) + +type Item struct { + addCategory chan string + XMLName xml.Name `xml:"item"` + Title string `xml:"title,omitempty"` + Link string `xml:"link,omitempty"` + Description string `xml:"description,omitempty"` + Author string `xml:"author,omitempty"` + Comments string `xml:"comments,omitempty"` + Enclosure *Enclosure + Guid string `xml:"guid,omitempty"` + PubDate string `xml:"pubDate,omitempty"` + Source *Source + Categories []string `xml:"category,omitempty"` + wg sync.WaitGroup +} + +func (i *Item) start() { + i.wg.Done() + + for category := range i.addCategory { + i.Categories = append(i.Categories, category) + } +} + +func initItem() *Item { + item := &Item{ + addCategory: make(chan string), + PubDate: time.Now().Format(time.RFC1123Z), + } + + item.wg.Add(1) + go item.start() + item.wg.Wait() + + return item +} + +func (i *Item) check() error { + if len(i.Title) == 0 && len(i.Description) == 0 { + return fmt.Errorf("error: neither title nor description set") + } + + if err := i.Enclosure.check(); err != nil { + return fmt.Errorf("error checking enclosure: %v", err) + } + if err := i.Source.check(); err != nil { + return fmt.Errorf("error checking source: %v", err) + } + + return nil +} + +func NewItem() *Item { + return initItem() +} + +func (i *Item) AddCategory(category string) { + i.addCategory <- category +} diff --git a/rss.go b/rss.go index 6e0e58c..d6c6fd6 100644 --- a/rss.go +++ b/rss.go @@ -3,7 +3,6 @@ package rss import ( "encoding/xml" "fmt" - "time" ) type Image struct { @@ -26,8 +25,8 @@ type TextInput struct { type Enclosure struct { XMLName xml.Name `xml:"enclosure"` Url string `xml:"url,attr"` - Lenght int `xml:"lenght,attr"` Type string `xml:"type,attr"` + Lenght int `xml:"lenght,attr"` } type Source struct { @@ -36,51 +35,6 @@ type Source struct { Value string } -type Item struct { - XMLName xml.Name `xml:"item"` - Title string `xml:"title,omitempty"` - Link string `xml:"link,omitempty"` - Description string `xml:"description,omitempty"` - Author string `xml:"author,omitempty"` - Categories []string `xml:"category,omitempty"` - Comments string `xml:"comments,omitempty"` - Enclosure *Enclosure - Guid string `xml:"guid,omitempty"` - PubDate time.Time `xml:"pubDate,omitempty"` - Source *Source -} - -type Channel struct { - XMLName xml.Name `xml:"channel"` - Title string `xml:"title"` - Link string `xml:"link"` - Description string `xml:"description"` - Language string `xml:"language,omitempty"` - Copyright string `xml:"copyright,omitempty"` - ManagingEditor string `xml:"managingEditor,omitempty"` - WebMaster string `xml:"webMaster,omitempty"` - PubDate time.Time `xml:"pubDate,omitempty"` - LastBuildDate time.Time `xml:"lastBuildDate,omitempty"` - Categories []string `xml:"category,omitempty"` - Generator string `xml:"generator,omitempty"` - Docs string `xml:"docs,omitempty"` - Cloud string `xml:"cloud,omitempty"` - Ttl int `xml:"ttl,omitempty"` - Image *Image - Rating string `xml:"rating,omitempty"` - TextInput *TextInput - SkipHours []int `xml:"skipHours,omitempty"` - SkipDays []int `xml:"skipDays,omitempty"` - Items []*Item -} - -type Feed struct { - XMLName xml.Name `xml:"rss"` - Version string `xml:"version,attr"` - ContentNamespace string `xml:"xmlns:content,attr"` - Channels []*Channel -} - func (i *Image) check() error { if len(i.Url) == 0 { return fmt.Errorf("error: url not set") @@ -137,67 +91,6 @@ func (s *Source) check() error { return nil } -func (i *Item) check() error { - if len(i.Title) == 0 && len(i.Description) == 0 { - return fmt.Errorf("error: neither title nor description set") - } - - if err := i.Enclosure.check(); err != nil { - return fmt.Errorf("error checking enclosure: %v", err) - } - if err := i.Source.check(); err != nil { - return fmt.Errorf("error checking source: %v", err) - } - - return nil -} - -func (c *Channel) check() error { - if len(c.Title) == 0 { - return fmt.Errorf("error: title not set") - } - if len(c.Link) == 0 { - return fmt.Errorf("error: link not set") - } - if len(c.Description) == 0 { - return fmt.Errorf("error: description not set") - } - - if err := c.Image.check(); err != nil { - return fmt.Errorf("error checking image: %v", err) - } - if err := c.TextInput.check(); err != nil { - return fmt.Errorf("error checking textInput: %v", err) - } - - for _, item := range c.Items { - if err := item.check(); err != nil { - return fmt.Errorf("error checking item: %v", err) - } - } - - return nil -} - -func (f *Feed) check() error { - f = &Feed{ - Version: "2.0", - ContentNamespace: "http://purl.org/rss/1.0/modules/content/", - } - - if f.Channels == nil { - return fmt.Errorf("error: feed has no channels") - } - - for _, channel := range f.Channels { - if err := channel.check(); err != nil { - return fmt.Errorf("error checking channel: %v", err) - } - } - - return nil -} - func (f *Feed) ToXML() (string, error) { if err := f.check(); err != nil { return "", fmt.Errorf("error checking RSS feed: %v", err)