Files
comitium/core/structs.go
T

168 lines
4.5 KiB
Go

// 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.
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
}
// Implement sort.Interface for Feed
// Len returns the length of Items.
func (f *Feed) Len() int {
return len(f.Items)
}
// Less compares Published of Items[i], Items[k] and returns true if Items[i] is
// before Items[j].
func (f *Feed) Less(i, j int) {
return f.Items[i].Published.Before(f.Items[j].Published)
}
// Swap swaps Items[i] and Items[j].
func (f *Feed) Swap(i, j int) {
f.Items[i], f.Items[j] = f.Items[j], f.Items[i]
}
// 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 for PageEntries
// Len returns the length of Entries
func (e *PageEntries) Len() int {
return len(e.Entries)
}
// Less compares the Published if Entries[i], Entries[j] and returns true if
// Entries[i] is /after/ Entries[j] (for reverse chronological sorting)
func (e *PageEntries) Less(i, j int) bool {
return e.Entries[i].Published.After(e.Entries[j].Published)
}
// Swap swaps Entries[i] and Entries[j]
func (e *PageEntries) Swap(i, j int) {
e.Entries[i], e.Entries[j] = e.Entries[j], e.Entries[i]
}