package rss import ( "encoding/xml" "fmt" ) type Image struct { XMLName xml.Name `xml:"image"` Url string `xml:"url"` Title string `xml:"title"` Link string `xml:"link"` Width int `xml:"width,omitempty"` // max: 144, default: 88 Height int `xml:"height,omitempty"` // max: 400, default: 31 } type TextInput struct { XMLName xml.Name `xml:"textInput"` Title string `xml:"title,omitempty"` Description string `xml:"description,omitempty"` Name string `xml:"name,omitempty"` Link string `xml:"link,omitempty"` } type Enclosure struct { XMLName xml.Name `xml:"enclosure"` Url string `xml:"url,attr"` Type string `xml:"type,attr"` Lenght int `xml:"lenght,attr"` } type Source struct { XMLName xml.Name `xml:"source"` Url string `xml:"url,attr"` Value string } type Content struct { XMLName xml.Name `xml:"content:encoded"` Value string `xml:",cdata"` } 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"` Comments string `xml:"comments,omitempty"` Enclosure *Enclosure Guid string `xml:"guid,omitempty"` PubDate string `xml:"pubDate,omitempty"` Source *Source Content *Content Categories []string `xml:"category,omitempty"` } 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 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"` } 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") } if len(i.Title) == 0 { return fmt.Errorf("error: title not set") } if len(i.Link) == 0 { return fmt.Errorf("error: link not set") } if i.Width == 0 || i.Width > 144 { i.Width = 88 } if i.Height == 0 || i.Height > 400 { i.Height = 31 } return nil } func (t *TextInput) check() error { if len(t.Title) == 0 && len(t.Description) == 0 && len(t.Name) == 0 && len(t.Link) == 0 { return fmt.Errorf("error: textInput is empty") } return nil } func (e *Enclosure) check() error { if len(e.Url) == 0 { return fmt.Errorf("error: url not set") } if e.Lenght == 0 { return fmt.Errorf("error: length not set or 0") } if len(e.Type) == 0 { return fmt.Errorf("error: type not set") } return nil } func (s *Source) check() error { if len(s.Url) == 0 { return fmt.Errorf("error: url not set") } if len(s.Value) == 0 { return fmt.Errorf("error: value not set") } return nil } func (c *Content) check() error { if len(c.Value) == 0 { return fmt.Errorf("error: value not set") } 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 i.Enclosure != nil { if err := i.Enclosure.check(); err != nil { return fmt.Errorf("error checking enclosure: %v", err) } } if i.Source != nil { if err := i.Source.check(); err != nil { return fmt.Errorf("error checking source: %v", err) } } if i.Content != nil { if err := i.Content.check(); err != nil { return fmt.Errorf("error checking content: %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 c.Image != nil { if err := c.Image.check(); err != nil { return fmt.Errorf("error checking image: %v", err) } } if c.TextInput != nil { 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 { 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 &Feed{ Version: "2.0", ContentNamespace: "http://purl.org/rss/1.0/modules/content/", Channels: make([]*Channel, 0), } } func (f *Feed) ToXML(encoding string) (string, error) { if err := f.check(); err != nil { return "", fmt.Errorf("error checking RSS feed: %v", err) } xml, err := xml.MarshalIndent(f, "", " ") if err != nil { return "", fmt.Errorf("error XML encoding feed: %v", err) } return `` + string(xml), nil }