a love letter to tangled (android, iOS, and a search API)
1package backfill
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "strings"
8)
9
10type seedEntry struct {
11 raw string
12 isDID bool
13 lineNo int
14}
15
16func parseSeedFile(path string) ([]seedEntry, error) {
17 f, err := os.Open(path)
18 if err != nil {
19 return nil, fmt.Errorf("open seed file: %w", err)
20 }
21 defer f.Close()
22
23 seen := map[string]bool{}
24 entries := make([]seedEntry, 0)
25 s := bufio.NewScanner(f)
26 lineNo := 0
27 for s.Scan() {
28 lineNo++
29 line := strings.TrimSpace(s.Text())
30 if line == "" || strings.HasPrefix(line, "#") {
31 continue
32 }
33 if seen[line] {
34 continue
35 }
36 seen[line] = true
37 entries = append(entries, seedEntry{
38 raw: line,
39 isDID: isDID(line),
40 lineNo: lineNo,
41 })
42 }
43 if err := s.Err(); err != nil {
44 return nil, fmt.Errorf("scan seed file: %w", err)
45 }
46 if len(entries) == 0 {
47 return nil, fmt.Errorf("seed file has no valid entries")
48 }
49 return entries, nil
50}
51
52func parseSeedInput(input string) ([]seedEntry, error) {
53 input = strings.TrimSpace(input)
54 if input == "" {
55 return nil, fmt.Errorf("seeds input is required")
56 }
57
58 if strings.Contains(input, ",") {
59 return parseSeedList(input)
60 }
61
62 info, err := os.Stat(input)
63 if err == nil && !info.IsDir() {
64 return parseSeedFile(input)
65 }
66
67 return parseSeedList(input)
68}
69
70func parseSeedList(list string) ([]seedEntry, error) {
71 parts := strings.Split(list, ",")
72 seen := map[string]bool{}
73 entries := make([]seedEntry, 0, len(parts))
74 for i, part := range parts {
75 value := strings.TrimSpace(part)
76 if value == "" {
77 continue
78 }
79 if seen[value] {
80 continue
81 }
82 seen[value] = true
83 entries = append(entries, seedEntry{
84 raw: value,
85 isDID: isDID(value),
86 lineNo: i + 1,
87 })
88 }
89 if len(entries) == 0 {
90 return nil, fmt.Errorf("seed list has no valid entries")
91 }
92 return entries, nil
93}
94
95func isDID(v string) bool {
96 return strings.HasPrefix(v, "did:")
97}