Skip to content

Commit

Permalink
feat(entity): Add method to sort feed entries for reading
Browse files Browse the repository at this point in the history
  • Loading branch information
bow committed Jan 29, 2024
1 parent 3a706bb commit 4abd362
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 0 deletions.
45 changes: 45 additions & 0 deletions internal/entity/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package entity

import (
"sort"
"strconv"
"time"

Expand Down Expand Up @@ -112,3 +113,47 @@ func FromTimestampPb(pb *timestamppb.Timestamp) *time.Time {
}

const defaultExportTitle = "neon export"

type compFunc[T any] func(v1, v2 T) int

type sorter[T any] struct {
items []T
compfs []compFunc[T]
}

func ordered[T any]() *sorter[T] {
return &sorter[T]{compfs: make([]compFunc[T], 0)}
}

func (s *sorter[T]) By(compf ...compFunc[T]) *sorter[T] {
s.compfs = append(s.compfs, compf...)
return s
}

func (s *sorter[T]) Len() int {
return len(s.items)
}

func (s *sorter[T]) Swap(i, j int) {
s.items[i], s.items[j] = s.items[j], s.items[i]
}

func (s *sorter[T]) Less(i, j int) bool {
p, q := s.items[i], s.items[j]
var k int
for k = 0; k < len(s.compfs)-1; k++ {
comp := s.compfs[k](p, q)
if comp < 0 {
return true
}
if comp > 0 {
return false
}
}
return s.compfs[k](p, q) < 0
}

func (s *sorter[T]) Sort(items []T) {
s.items = items
sort.Sort(s)
}
47 changes: 47 additions & 0 deletions internal/entity/feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,53 @@ func (f *Feed) NumEntriesUnread() int {
return f.NumEntriesTotal() - f.NumEntriesRead()
}

// Sort entries by read status (unread first), update date (oldest first), and published date
// (oldest first).
func (f *Feed) SortEntries() { // nolint:revive
sortDate := func(e *Entry) *time.Time {
if e.Updated != nil {
return e.Updated
}
if e.Published != nil {
return e.Published
}
return nil
}
isRead := func(e1, e2 *Entry) int {
if e1.IsRead && !e2.IsRead {
return 1
}
if !e1.IsRead && e2.IsRead {
return -1
}
return 0
}
date := func(e1, e2 *Entry) int {
d1 := sortDate(e1)
d2 := sortDate(e2)
if d1 != nil && d2 != nil {
if d1.Before(*d2) {
return 1
}
if d2.Before(*d1) {
return -1
}
return 0
}
if d1 != nil {
return -1
}
if d2 != nil {
return 1
}
return 0
}

ordered[*Entry]().
By(isRead, date).
Sort(f.Entries)
}

func (f *Feed) Outline() (*opml.Outline, error) {
outl := opml.Outline{
Text: f.Title,
Expand Down
76 changes: 76 additions & 0 deletions internal/entity/feed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) 2024 Wibowo Arindrarto <[email protected]>
// SPDX-License-Identifier: BSD-3-Clause

package entity

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

var (
now = time.Now()
oneHourAgo = now.Add(-1 * time.Hour)
yesterday = now.Add(-1 * 24 * time.Hour)
threeDaysAgo = now.Add(-3 * 24 * time.Hour)
lastWeek = now.Add(-7 * 24 * time.Hour)
twoWeeksAgo = now.Add(-14 * 24 * time.Hour)
)

func TestFeedSortEntries(t *testing.T) {
a := assert.New(t)
f := Feed{
Entries: []*Entry{
{
Title: "A",
IsRead: true,
Published: &twoWeeksAgo,
Updated: nil,
},
{
Title: "B",
IsRead: false,
Published: &lastWeek,
Updated: &yesterday,
},
{
Title: "C",
IsRead: false,
Published: &lastWeek,
Updated: &yesterday,
},
{
Title: "D",
IsRead: false,
Published: nil,
Updated: &threeDaysAgo,
},
{
Title: "E",
IsRead: false,
Published: &oneHourAgo,
Updated: nil,
},
{
Title: "F",
IsRead: true,
Published: nil,
Updated: nil,
},
},
}
f.SortEntries()

want := []string{"E", "B", "C", "D", "A", "F"}
got := make([]string, len(f.Entries))

for i, entry := range f.Entries {
title := entry.Title
got[i] = title
}

a.Len(got, 6)
a.Equal(want, got)
}

0 comments on commit 4abd362

Please sign in to comment.