implement core package with structs, etc.
This commit is contained in:
+144
@@ -0,0 +1,144 @@
|
||||
// Copyright (C) 2021 nytpu
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// For more license details, see LICENSE or <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||
|
||||
// This file incorporates code from makeworlds's amfora:
|
||||
// https://github.com/makeworld-the-better-one/amfora/blob/master/subscriptions/structs.go
|
||||
// Original source is Copyright 2020 makeworld and is licensed under the
|
||||
// terms of the GNU General Public License Version 3.0.
|
||||
// https://www.gnu.org/licenses/gpl-3.0-standalone.html
|
||||
|
||||
// package core implements the structs and other necessary resources for storing
|
||||
// and managing feeds and pages
|
||||
package core
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mmcdole/gofeed"
|
||||
)
|
||||
|
||||
// jsonData is used for both reading from json and for storing the global feeds
|
||||
// map. contains read mutexes so concurrency can be used.
|
||||
type jsonData struct {
|
||||
Feeds map[url.URL]*Feed `json:"feeds,omitempty"`
|
||||
Pages map[url.URL]*Page `json:"pages,omitempty"`
|
||||
|
||||
feedsMu *sync.RWMutex
|
||||
pagesMu *sync.RWMutex
|
||||
}
|
||||
|
||||
// Feed holds all the necessary fields when storing a feed
|
||||
type Feed struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Link *url.URL `json:"link,omitempty"` // link to the human-readable page for the feed. same as FeedLink for gemini feeds.
|
||||
FeedLink *url.URL `json:"feedlink,omitempty"` // link to the feed itself. if empty, use Link instead.
|
||||
Updated time.Time `json:"updated,omitempty"`
|
||||
Items []*Item `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// Item is a single item from a subscripton
|
||||
type Item struct {
|
||||
Title string `json:"title,omitempty"` // if empty, use Link as title
|
||||
Content string `json:"content,omitempty"`
|
||||
Link *url.URL `json:"link,omitempty"`
|
||||
Published time.Time `json:"updated,omitempty"`
|
||||
}
|
||||
|
||||
// Page holds all the necessary fields when storing a page being watched for
|
||||
// changes.
|
||||
type Page struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Link *url.URL `json:"link,omitempty"`
|
||||
Updated time.Time `json:"updated,omitempty"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
}
|
||||
|
||||
// gofeedConvert converts a gofeed.Feed into a Feed struct
|
||||
func GofeedConvert(f *gofeed.Feed, title string) (*Feed, error) {
|
||||
new := &Feed{}
|
||||
|
||||
if title != "" {
|
||||
new.Title = title
|
||||
} else {
|
||||
new.Title = f.Title
|
||||
}
|
||||
new.Updated = *f.UpdatedParsed
|
||||
var err error
|
||||
new.Link, err = url.ParseRequestURI(f.Link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
new.FeedLink, err = url.ParseRequestURI(f.FeedLink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range f.Items {
|
||||
item := &Item{}
|
||||
item.Title = v.Title
|
||||
item.Content = v.Content
|
||||
item.Published = *v.PublishedParsed
|
||||
item.Link, err = url.ParseRequestURI(f.FeedLink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
new.Items = append(new.Items, item)
|
||||
}
|
||||
|
||||
return new, nil
|
||||
}
|
||||
|
||||
// Lock locks both feed and page mutexes.
|
||||
func (j *jsonData) Lock() {
|
||||
j.feedsMu.Lock()
|
||||
j.pagesMu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks both feed and page mutexes.
|
||||
func (j *jsonData) Unlock() {
|
||||
j.feedsMu.Unlock()
|
||||
j.pagesMu.Unlock()
|
||||
}
|
||||
|
||||
// RLock read-locks both feed and page mutexes.
|
||||
func (j *jsonData) RLock() {
|
||||
j.feedsMu.RLock()
|
||||
j.pagesMu.RLock()
|
||||
}
|
||||
|
||||
// RUnlock read-unlocks both feed and page mutexes.
|
||||
func (j *jsonData) RUnlock() {
|
||||
j.feedsMu.RUnlock()
|
||||
j.pagesMu.RUnlock()
|
||||
}
|
||||
|
||||
// PageEntry is a single item on a subscriptions page. It is used for both feeds
|
||||
// and pages.
|
||||
type PageEntry struct {
|
||||
Prefix string // whatever goes before the post title to identify the feed.
|
||||
Title string // article/entry title
|
||||
URL string
|
||||
Published time.Time
|
||||
}
|
||||
|
||||
// PageEntries is reverse chronological list of entries used when generating subs.
|
||||
type PageEntries struct {
|
||||
Entries []*PageEntry
|
||||
}
|
||||
|
||||
// Implement sort.Interface
|
||||
func (e *PageEntries) Len() int {
|
||||
return len(e.Entries)
|
||||
}
|
||||
|
||||
func (e *PageEntries) Less(i, j int) bool {
|
||||
return e.Entries[i].Published.After(e.Entries[j].Published)
|
||||
}
|
||||
|
||||
func (e *PageEntries) Swap(i, j int) {
|
||||
e.Entries[i], e.Entries[j] = e.Entries[j], e.Entries[i]
|
||||
}
|
||||
Reference in New Issue
Block a user