/*
Copyright (c) 2019 Ben Morrison (gbmor)
This file is part of Registry.
Registry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Registry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Registry. If not, see <https://www.gnu.org/licenses/>.
*/
package registry // import "git.sr.ht/~gbmor/getwtxt/registry"
import (
"fmt"
"sort"
"strings"
"time"
)
// QueryUser checks the Registry for usernames
// or user URLs that contain the term provided as an argument. Entries
// are returned sorted by the date they were added to the Registry. If
// the argument provided is blank, return all users.
func (registry *Registry) QueryUser(term string) ([]string, error) {
if registry == nil {
return nil, fmt.Errorf("can't query empty registry for user")
}
term = strings.ToLower(term)
timekey := NewTimeMap()
keys := make(TimeSlice, 0)
var users []string
registry.Mu.RLock()
defer registry.Mu.RUnlock()
for k, v := range registry.Users {
if registry.Users[k] == nil {
continue
}
v.Mu.RLock()
if strings.Contains(strings.ToLower(v.Nick), term) || strings.Contains(strings.ToLower(k), term) {
thetime, err := time.Parse(time.RFC3339, v.Date)
if err != nil {
v.Mu.RUnlock()
continue
}
timekey[thetime] = v.Nick + "\t" + k + "\t" + v.Date + "\n"
keys = append(keys, thetime)
}
v.Mu.RUnlock()
}
sort.Sort(keys)
for _, e := range keys {
users = append(users, timekey[e])
}
return users, nil
}
// QueryInStatus returns all statuses in the Registry
// that contain the provided substring (tag, mention URL, etc).
func (registry *Registry) QueryInStatus(substring string) ([]string, error) {
if substring == "" {
return nil, fmt.Errorf("cannot query for empty tag")
} else if registry == nil {
return nil, fmt.Errorf("can't query statuses of empty registry")
}
statusmap := make([]TimeMap, 0)
registry.Mu.RLock()
defer registry.Mu.RUnlock()
for _, v := range registry.Users {
statusmap = append(statusmap, v.FindInStatus(substring))
}
sorted, err := SortByTime(statusmap...)
if err != nil {
return nil, err
}
return sorted, nil
}
// QueryAllStatuses returns all statuses in the Registry
// as a slice of strings sorted by timestamp.
func (registry *Registry) QueryAllStatuses() ([]string, error) {
if registry == nil {
return nil, fmt.Errorf("can't get latest statuses from empty registry")
}
statusmap, err := registry.GetStatuses()
if err != nil {
return nil, err
}
sorted, err := SortByTime(statusmap)
if err != nil {
return nil, err
}
if sorted == nil {
sorted = make([]string, 1)
}
return sorted, nil
}
// ReduceToPage returns the passed 'page' worth of output.
// One page is twenty items. For example, if 2 is passed,
// it will return data[20:40]. According to the twtxt
// registry specification, queries should accept a "page"
// value.
func ReduceToPage(page int, data []string) []string {
end := 20 * page
if end > len(data) || end < 1 {
end = len(data)
}
beg := end - 20
if beg > len(data)-1 || beg < 0 {
beg = 0
}
return data[beg:end]
}
// FindInStatus takes a user's statuses and looks for a given substring.
// Returns the statuses that include the substring as a TimeMap.
func (userdata *User) FindInStatus(substring string) TimeMap {
if userdata == nil {
return nil
} else if len(substring) > 140 {
return nil
}
substring = strings.ToLower(substring)
statuses := NewTimeMap()
userdata.Mu.RLock()
defer userdata.Mu.RUnlock()
for k, e := range userdata.Status {
if _, ok := userdata.Status[k]; !ok {
continue
}
parts := strings.Split(strings.ToLower(e), "\t")
if strings.Contains(parts[3], substring) {
statuses[k] = e
}
}
return statuses
}
// SortByTime returns a string slice of the query results,
// sorted by timestamp in descending order (newest first).
func SortByTime(tm ...TimeMap) ([]string, error) {
if tm == nil {
return nil, fmt.Errorf("can't sort nil TimeMaps")
}
var times = make(TimeSlice, 0)
var data []string
for _, e := range tm {
for k := range e {
times = append(times, k)
}
}
sort.Sort(times)
for k := range tm {
for _, e := range times {
if _, ok := tm[k][e]; ok {
data = append(data, tm[k][e])
}
}
}
return data, nil
}