Blob


1 package fs
3 import (
4 "errors"
5 "fmt"
6 "io"
7 "io/fs"
8 "path"
9 "strconv"
10 "strings"
11 "time"
13 "olowe.co/apub/lemmy"
14 )
16 type FS struct {
17 Client *lemmy.Client
18 started bool
19 }
21 func (fsys *FS) start() error {
22 if fsys.Client == nil {
23 fsys.Client = &lemmy.Client{}
24 }
25 fsys.started = true
26 return nil
27 }
29 func (fsys *FS) Open(name string) (fs.File, error) {
30 if !fs.ValidPath(name) {
31 return nil, &fs.PathError{"open", name, fs.ErrInvalid}
32 } else if strings.Contains(name, `\`) {
33 return nil, &fs.PathError{"open", name, fs.ErrInvalid}
34 }
35 name = path.Clean(name)
37 if !fsys.started {
38 if err := fsys.start(); err != nil {
39 return nil, fmt.Errorf("start fs: %w", err)
40 }
41 }
42 if name == "." {
43 return fsys.openRoot()
44 }
46 elems := strings.Split(name, "/")
47 // We've only got communities, then posts/comments.
48 // Anything deeper cannot exist.
49 if len(elems) > 3 {
50 return nil, &fs.PathError{"open", name, fs.ErrNotExist}
51 }
53 community, _, err := fsys.Client.LookupCommunity(elems[0])
54 if errors.Is(err, lemmy.ErrNotFound) {
55 return nil, &fs.PathError{"open", name, fs.ErrNotExist}
56 } else if err != nil {
57 return nil, &fs.PathError{"open", name, err}
58 }
59 if len(elems) == 1 {
60 return &lFile{
61 info: &community,
62 buf: io.NopCloser(strings.NewReader(community.Name())),
63 client: fsys.Client,
64 }, nil
65 }
67 id, err := strconv.Atoi(elems[1])
68 if err != nil {
69 return nil, &fs.PathError{"open", name, fmt.Errorf("bad post id")}
70 }
71 post, err := fsys.Client.LookupPost(id)
72 if errors.Is(err, lemmy.ErrNotFound) {
73 return nil, &fs.PathError{"open", name, fs.ErrNotExist}
74 } else if err != nil {
75 return nil, &fs.PathError{"open", name, err}
76 }
77 if len(elems) == 2 {
78 return &lFile{
79 info: &post,
80 buf: io.NopCloser(strings.NewReader(post.Name())),
81 client: fsys.Client,
82 }, nil
83 }
84 if elems[2] == "post" {
85 info, err := postFile(&post).Stat()
86 if err != nil {
87 return nil, &fs.PathError{"open", name, fmt.Errorf("prepare post file info: %w", err)}
88 }
89 return &lFile{
90 info: info,
91 buf: io.NopCloser(postText(&post)),
92 client: fsys.Client,
93 }, nil
94 }
96 id, err = strconv.Atoi(elems[2])
97 if err != nil {
98 return nil, &fs.PathError{"open", name, fmt.Errorf("bad comment id")}
99 }
100 comment, err := fsys.Client.LookupComment(id)
101 if errors.Is(err, lemmy.ErrNotFound) {
102 return nil, &fs.PathError{"open", name, fs.ErrNotExist}
103 } else if err != nil {
104 return nil, &fs.PathError{"open", name, err}
106 return &lFile{
107 info: &comment,
108 buf: io.NopCloser(commentText(&comment)),
109 client: fsys.Client,
110 }, nil
113 func (fsys *FS) openRoot() (fs.File, error) {
114 dirinfo := new(dirInfo)
115 communities, err := fsys.Client.Communities(lemmy.ListAll)
116 if err != nil {
117 return nil, err
119 for _, c := range communities {
120 c := c
121 dent := fs.FileInfoToDirEntry(&c)
122 dirinfo.entries = append(dirinfo.entries, dent)
124 return &dummy{
125 name: ".",
126 mode: fs.ModeDir | 0444,
127 contents: []byte("hello, world!"),
128 dirinfo: dirinfo,
129 mtime: time.Now(),
130 }, nil