diff options
Diffstat (limited to 'registry/query.go')
-rw-r--r-- | registry/query.go | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/registry/query.go b/registry/query.go new file mode 100644 index 0000000..604b974 --- /dev/null +++ b/registry/query.go @@ -0,0 +1,196 @@ +/* +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 +} |