From f6313f44653ccb0d6e7b92319ade907436802e72 Mon Sep 17 00:00:00 2001 From: nytpu Date: Tue, 16 Mar 2021 22:42:51 -0600 Subject: [PATCH] implement core package with structs, etc. --- core/structs.go | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 core/structs.go diff --git a/core/structs.go b/core/structs.go new file mode 100644 index 0000000..eec1473 --- /dev/null +++ b/core/structs.go @@ -0,0 +1,144 @@ +// Copyright (C) 2021 nytpu +// SPDX-License-Identifier: AGPL-3.0-or-later +// For more license details, see LICENSE or . + +// 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] +}