]> Sergey Matveev's repositories - linksexp.git/blob - main.go
0657c0a12783583a30ffd98c156a0d2b1247790a
[linksexp.git] / main.go
1 /*
2 linksexp -- Texinfo/XBEL/OPML autogeneration from recfile bookmark
3 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package main
19
20 import (
21         "flag"
22         "fmt"
23         "io"
24         "os"
25         "sort"
26         "strings"
27         "time"
28
29         "go.cypherpunks.ru/recfile"
30 )
31
32 type ByTitle []map[string][]string
33
34 func (a ByTitle) Len() int      { return len(a) }
35 func (a ByTitle) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
36 func (a ByTitle) Less(i, j int) bool {
37         return strings.Compare(a[i]["Title"][0], a[j]["Title"][0]) < 0
38 }
39
40 func main() {
41         doXBEL := flag.Bool("xbel", false, "Make XBEL")
42         doOPML := flag.Bool("opml", false, "Make OPML")
43         flag.Parse()
44
45         r := recfile.NewReader(os.Stdin)
46         if *doOPML {
47                 data := make([]map[string][]string, 0)
48                 for {
49                         m, err := r.NextMapWithSlice()
50                         if err == io.EOF {
51                                 break
52                         }
53                         if err != nil {
54                                 panic(err)
55                         }
56                         if m["%rec"] != nil {
57                                 continue
58                         }
59                         sort.Strings(m["Category"])
60                         data = append(data, m)
61                 }
62                 sort.Sort(ByTitle(data))
63                 opml(data)
64                 os.Exit(0)
65         }
66
67         data := make(map[string][]map[string][]string)
68         for {
69                 m, err := r.NextMapWithSlice()
70                 if err == io.EOF {
71                         break
72                 }
73                 if err != nil {
74                         panic(err)
75                 }
76                 if m["%rec"] != nil {
77                         continue
78                 }
79                 sort.Strings(m["Category"])
80                 if cs := m["Category"]; len(cs) == 0 {
81                         data["Uncategorized"] = append(data["Uncategorized"], m)
82                 } else {
83                         for _, cat := range cs {
84                                 data[cat] = append(data[cat], m)
85                         }
86                 }
87         }
88         cats := make([]string, 0, len(data))
89         for c := range data {
90                 cats = append(cats, c)
91                 sort.Sort(ByTitle(data[c]))
92         }
93         sort.Strings(cats)
94
95         if *doXBEL {
96                 xbel(cats, data)
97                 os.Exit(0)
98         }
99
100         fmt.Println("Updated:", time.Now().Format(time.RFC3339))
101         fmt.Println("@table @strong")
102         for _, cat := range cats {
103                 fmt.Println("@item", cat)
104                 fmt.Println("@multitable @columnfractions .05 .8 .1 .05")
105                 ents := data[cat]
106                 for n, ent := range ents {
107                         catsOther := make([]string, 0)
108                         for _, c := range ent["Category"] {
109                                 if c != cat {
110                                         catsOther = append(catsOther, c)
111                                 }
112                         }
113                         var note string
114                         if len(ent["Note"]) > 0 {
115                                 note = "(" + ent["Note"][0] + ")"
116                         }
117                         fmt.Printf(
118                                 "  @item %d @tab @url{%s, %s} %s @tab %s @tab\n",
119                                 n, ent["URL"][0], ent["Title"][0], note,
120                                 strings.Join(catsOther, ", "),
121                         )
122                         switch feeds := ent["Feed"]; len(feeds) {
123                         case 0:
124                                 fmt.Printf("    @emph{STATIC}\n")
125                         case 1:
126                                 fmt.Printf("    @url{%s, feed}\n", feeds[0])
127                         default:
128                                 for i, feed := range feeds {
129                                         fmt.Printf("    @url{%s, feed%d}\n", feed, i)
130                                 }
131                         }
132                 }
133                 fmt.Println("@end multitable")
134         }
135         fmt.Println("@end table")
136 }