intake/core/item.go

119 lines
2.6 KiB
Go

package core
import (
"database/sql/driver"
"encoding/json"
"fmt"
"log"
"math/rand"
"time"
)
type Actions map[string]json.RawMessage
func (a Actions) Value() (driver.Value, error) {
return json.Marshal(a)
}
func (a *Actions) Scan(value interface{}) error {
return json.Unmarshal([]byte(value.(string)), a)
}
type Item struct {
Source string `json:"source"`
Id string `json:"id"`
Created int `json:"created"`
Active bool `json:"active"`
Title string `json:"title"`
Author string `json:"author"`
Body string `json:"body"`
Link string `json:"link"`
Time int `json:"time"`
Ttl int `json:"ttl"`
Ttd int `json:"ttd"`
Tts int `json:"tts"`
Action Actions `json:"action"`
}
func (item Item) TtlTime() time.Time {
return time.Unix(int64(item.Created)+int64(item.Ttl), 0)
}
func (item Item) TtdTime() time.Time {
return time.Unix(int64(item.Created)+int64(item.Ttd), 0)
}
func (item Item) TtsTime() time.Time {
return time.Unix(int64(item.Created)+int64(item.Tts), 0)
}
func (item Item) Visible() bool {
now := time.Now() // TODO pass this value in
return item.Active && now.After(item.TtsTime())
}
// Whether an item that no longer appears in a fetch can be deleted.
func (item Item) Deletable(now time.Time) bool {
if item.Ttl != 0 && item.TtlTime().After(now) {
return false
}
if item.Ttd != 0 && item.TtdTime().Before(now) {
return true
}
return !item.Active
}
func ItemsAreEqual(first Item, second Item) bool {
// Hacky but easy to use
return fmt.Sprintf("%#v", first) == fmt.Sprintf("%#v", second)
}
func FormatAsHeadline(item Item) string {
title := item.Title
if title == "" {
title = item.Id
}
return title
}
func FormatAsJson(item Item) string {
data, err := json.Marshal(item)
if err != nil {
log.Fatalf("error: failed to serialize %s/%s: %v", item.Source, item.Id, err)
}
return string(data)
}
func FormatAsShort(item Item) string {
return fmt.Sprintf("%s/%s", item.Source, item.Id)
}
func FormatAs(format string) (func(item Item) string, error) {
switch format {
case "headlines":
return FormatAsHeadline, nil
case "json":
return FormatAsJson, nil
case "short":
return FormatAsShort, nil
default:
return nil, fmt.Errorf("invalid format '%s'", format)
}
}
var AvailableFormats = map[string]string{
"headlines": "Only item titles",
"json": "Full item JSON",
"short": "Item source and id",
}
const hexDigits = "0123456789abcdef"
func RandomHex(n int) string {
bytes := make([]byte, n)
for i := range bytes {
bytes[i] = hexDigits[rand.Intn(len(hexDigits))]
}
return string(bytes)
}