summary refs log tree commit diff stats
path: root/registry/query.go
diff options
context:
space:
mode:
Diffstat (limited to 'registry/query.go')
-rw-r--r--registry/query.go196
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
+}