First implementation of channels for safe access
This commit is contained in:
		
							
								
								
									
										104
									
								
								channel.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								channel.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								feed.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								feed.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								item.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								item.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										109
									
								
								rss.go
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								rss.go
									
									
									
									
									
								
							| @@ -3,7 +3,6 @@ package rss | |||||||
| import ( | import ( | ||||||
| 	"encoding/xml" | 	"encoding/xml" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"time" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Image struct { | type Image struct { | ||||||
| @@ -26,8 +25,8 @@ type TextInput struct { | |||||||
| type Enclosure struct { | type Enclosure struct { | ||||||
| 	XMLName xml.Name `xml:"enclosure"` | 	XMLName xml.Name `xml:"enclosure"` | ||||||
| 	Url     string   `xml:"url,attr"` | 	Url     string   `xml:"url,attr"` | ||||||
| 	Lenght  int      `xml:"lenght,attr"` |  | ||||||
| 	Type    string   `xml:"type,attr"` | 	Type    string   `xml:"type,attr"` | ||||||
|  | 	Lenght  int      `xml:"lenght,attr"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type Source struct { | type Source struct { | ||||||
| @@ -36,51 +35,6 @@ type Source struct { | |||||||
| 	Value   string | 	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 { | func (i *Image) check() error { | ||||||
| 	if len(i.Url) == 0 { | 	if len(i.Url) == 0 { | ||||||
| 		return fmt.Errorf("error: url not set") | 		return fmt.Errorf("error: url not set") | ||||||
| @@ -137,67 +91,6 @@ func (s *Source) check() error { | |||||||
| 	return nil | 	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) { | func (f *Feed) ToXML() (string, error) { | ||||||
| 	if err := f.check(); err != nil { | 	if err := f.check(); err != nil { | ||||||
| 		return "", fmt.Errorf("error checking RSS feed: %v", err) | 		return "", fmt.Errorf("error checking RSS feed: %v", err) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user