hnblogs/hnblogs.go

103 lines
2.1 KiB
Go

package hnblogs
import (
"encoding/json"
"fmt"
"math/rand"
"net/http"
"sync"
)
const BlogsURL = "https://raw.githubusercontent.com/surprisetalk/blogs.hn/main/blogs.json"
// Blog represents a single blog entry.
type Blog struct {
URL string `json:"url"`
Title string `json:"title"`
About string `json:"about"`
Now string `json:"now"`
Feed string `json:"feed"`
Desc string `json:"desc"`
}
var (
blogs []Blog
fetchError error
once sync.Once
)
// FetchBlogs fetches the list of blogs and memoizes it in RAM.
func FetchBlogs() ([]Blog, error) {
once.Do(func() {
resp, err := http.Get(BlogsURL)
if err != nil {
fetchError = fmt.Errorf("failed to fetch blogs: %v", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fetchError = fmt.Errorf("failed to fetch blogs: status code %d", resp.StatusCode)
return
}
var fetchedBlogs []Blog
if err := json.NewDecoder(resp.Body).Decode(&fetchedBlogs); err != nil {
fetchError = fmt.Errorf("failed to decode blogs JSON: %v", err)
return
}
blogs = fetchedBlogs
})
return blogs, fetchError
}
// GetBlogs returns the memoized list of blogs.
func GetBlogs() ([]Blog, error) {
return FetchBlogs()
}
// RandomBlog returns a random blog from the list of blogs.
func RandomBlog() (Blog, error) {
blogs, err := GetBlogs()
if err != nil {
return Blog{}, err
}
return blogs[rand.Intn(len(blogs))], nil
}
// RandomBlogs returns n random blogs from the list of blogs.
func RandomBlogs(n int) ([]Blog, error) {
blogs, err := GetBlogs()
if err != nil {
return nil, err
}
if n <= 0 || n > len(blogs) {
return nil, fmt.Errorf("invalid number of blogs requested")
}
selected := make([]Blog, n)
for i := range selected {
selected[i] = blogs[rand.Intn(len(blogs))]
}
return selected, nil
}
// NthBlog returns the nth blog from the list of blogs.
func NthBlog(n int) (Blog, error) {
blogs, err := GetBlogs()
if err != nil {
return Blog{}, err
}
if n < 0 || n >= len(blogs) {
return Blog{}, fmt.Errorf("index out of range")
}
return blogs[n], nil
}