shrtfile.go in go-shrt

at master

1// See LICENSE file for copyright and license details
2
3package shrt
4
5import (
6 "bufio"
7 "fmt"
8 "io/fs"
9 "strings"
10 "sync"
11)
12
13// ShrtType is the type of a ShrtFile entry. Their textual
14// representations within the ShrtFile are listed in [NoneType].
15type ShrtType int
16
17const (
18 NoneType ShrtType = iota
19 ShortLink // shrtlnk
20 GoGet // goget
21)
22
23// ShrtEntry is a ShrtFile entry.
24type ShrtEntry struct {
25 URL string
26 Type ShrtType
27}
28
29// The ShrtFile struct contains the data read from a specially-formatted
30// file. The syntax of the file is human readable. Each line
31// represents a key-value pair. The key is everything to the left
32// of the first equals sign, and the value is everything to the
33// right. The value is then split around the first occurence of the
34// colon character, with the left side representing the type, and the
35// right side representing the URL. Whitespace is trimmed from the
36// beginning and end of all fields.
37//
38// ShrtFile is safe for concurrent use across multiple goroutines.
39type ShrtFile struct {
40 m map[string]ShrtEntry
41 mux sync.RWMutex
42}
43
44// The NewShrtFile function returns a new ShrtFile.
45func NewShrtFile() *ShrtFile {
46 return &ShrtFile{m: make(map[string]ShrtEntry)}
47}
48
49// The ReadShrtFile function reads an existing ShrtFile from f and
50// returns a pointer to a ShrtFile object. The provided file is closed
51// before returning.
52func (s *ShrtFile) ReadShrtFile(f fs.File) error {
53 defer f.Close()
54 s.mux.Lock()
55 defer s.mux.Unlock()
56
57 s.m = make(map[string]ShrtEntry)
58
59 scnr := bufio.NewScanner(f)
60
61 for scnr.Scan() {
62 tok := strings.SplitN(scnr.Text(), "=", 2)
63 if _, ok := s.m[strings.Trim(tok[0], " ")]; ok {
64 return fmt.Errorf("repeat key: %s", tok[0])
65 }
66 if len(tok) != 2 {
67 return fmt.Errorf("invalid syntax: %s", scnr.Text())
68 }
69 key := strings.TrimSpace(tok[0])
70 tok = strings.SplitN(tok[1], ":", 2)
71 if len(tok) != 2 {
72 return fmt.Errorf("invalid syntax: %s", scnr.Text())
73 }
74 var typ ShrtType
75 switch strings.TrimSpace(tok[0]) {
76 case "shrtlnk":
77 typ = ShortLink
78 case "goget":
79 typ = GoGet
80 default:
81 return fmt.Errorf("unrecognized type: %s", tok[0])
82 }
83 s.m[key] = ShrtEntry{
84 Type: typ,
85 URL: strings.TrimSpace(tok[1]),
86 }
87 }
88 return nil
89}
90
91// The Get method gets the value of the specified key. If the key
92// does not exist, an error is returned.
93func (s *ShrtFile) Get(key string) (ShrtEntry, error) {
94 s.mux.RLock()
95 defer s.mux.RUnlock()
96 entry, ok := s.m[key]
97 if !ok {
98 entry.Type = NoneType
99 return entry, fmt.Errorf("key not found: %s", key)
100 }
101 return entry, nil
102}